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,617 @@
1
+ """Service for extracting API documentation from Python source code."""
2
+
3
+ import ast
4
+ import inspect
5
+ import re
6
+ import typing as t
7
+ from pathlib import Path
8
+
9
+ from acb.console import Console
10
+ from acb.depends import Inject, depends
11
+
12
+ from ..models.protocols import APIExtractorProtocol
13
+ from .regex_patterns import SAFE_PATTERNS
14
+
15
+
16
+ class PythonDocstringParser:
17
+ """Parser for extracting structured information from Python docstrings."""
18
+
19
+ def __init__(self) -> None:
20
+ # Regex patterns for different docstring styles using safe patterns
21
+ self.google_param_pattern = SAFE_PATTERNS[
22
+ "extract_google_docstring_params"
23
+ ]._get_compiled_pattern()
24
+ self.sphinx_param_pattern = SAFE_PATTERNS[
25
+ "extract_sphinx_docstring_params"
26
+ ]._get_compiled_pattern()
27
+ self.returns_pattern = SAFE_PATTERNS[
28
+ "extract_docstring_returns"
29
+ ]._get_compiled_pattern()
30
+
31
+ def parse_docstring(self, docstring: str | None) -> dict[str, t.Any]:
32
+ """Parse a docstring and extract structured information."""
33
+ if not docstring:
34
+ return {"description": "", "parameters": {}, "returns": "", "raises": []}
35
+
36
+ docstring = inspect.cleandoc(docstring)
37
+
38
+ # Extract main description (first paragraph)
39
+ lines = docstring.split("\n")
40
+ description_lines = []
41
+ for line in lines:
42
+ if line.strip() and not self._is_section_header(line):
43
+ description_lines.append(line)
44
+ elif description_lines:
45
+ break
46
+
47
+ description = "\n".join(description_lines).strip()
48
+
49
+ # Extract parameters
50
+ parameters = self._extract_parameters(docstring)
51
+
52
+ # Extract returns
53
+ returns = self._extract_returns(docstring)
54
+
55
+ # Extract raises information
56
+ raises = self._extract_raises(docstring)
57
+
58
+ return {
59
+ "description": description,
60
+ "parameters": parameters,
61
+ "returns": returns,
62
+ "raises": raises,
63
+ }
64
+
65
+ def _is_section_header(self, line: str) -> bool:
66
+ """Check if a line is a docstring section header."""
67
+ line = line.strip().lower()
68
+ headers = [
69
+ "args:",
70
+ "arguments:",
71
+ "parameters:",
72
+ "param:",
73
+ "returns:",
74
+ "yields:",
75
+ "raises:",
76
+ "note:",
77
+ "example:",
78
+ ]
79
+ return any(line.startswith(header) for header in headers)
80
+
81
+ def _extract_parameters(self, docstring: str) -> dict[str, str]:
82
+ """Extract parameter documentation from docstring."""
83
+ parameters = {}
84
+
85
+ # Try Google style first
86
+ google_matches = self.google_param_pattern.findall(docstring)
87
+ for param_name, param_desc in google_matches:
88
+ parameters[param_name] = param_desc.strip()
89
+
90
+ # Try Sphinx style if no Google style found
91
+ if not parameters:
92
+ sphinx_matches = self.sphinx_param_pattern.findall(docstring)
93
+ for param_name, param_desc in sphinx_matches:
94
+ parameters[param_name] = param_desc.strip()
95
+
96
+ return parameters
97
+
98
+ def _extract_returns(self, docstring: str) -> str:
99
+ """Extract return value documentation from docstring."""
100
+ match = self.returns_pattern.search(docstring)
101
+ if match:
102
+ return match.group(1).strip()
103
+ return ""
104
+
105
+ def _extract_raises(self, docstring: str) -> list[str]:
106
+ """Extract exception documentation from docstring."""
107
+ raises_pattern = re.compile( # REGEX OK: exception extraction
108
+ r"(?:Raises?|Raise):\s*(.+?)(?=\n\n|\n\w+:|\Z)", re.MULTILINE | re.DOTALL
109
+ )
110
+ match = raises_pattern.search(docstring)
111
+ if match:
112
+ raises_text = match.group(1).strip()
113
+ # Split by lines and clean up
114
+ raises_list = [
115
+ line.strip() for line in raises_text.split("\n") if line.strip()
116
+ ]
117
+ return raises_list
118
+ return []
119
+
120
+
121
+ class APIExtractorImpl(APIExtractorProtocol):
122
+ """Implementation of API documentation extraction from source code."""
123
+
124
+ @depends.inject
125
+ def __init__(self, console: Inject[Console]) -> None:
126
+ self.console = console
127
+ self.docstring_parser = PythonDocstringParser()
128
+
129
+ def extract_from_python_files(self, files: list[Path]) -> dict[str, t.Any]:
130
+ """Extract API documentation from Python files."""
131
+ api_data: dict[str, t.Any] = {
132
+ "modules": {},
133
+ "classes": {},
134
+ "functions": {},
135
+ "protocols": {},
136
+ }
137
+
138
+ for file_path in files:
139
+ if not file_path.exists() or file_path.suffix != ".py":
140
+ continue
141
+
142
+ try:
143
+ source_code = Path(file_path).read_text(encoding="utf-8")
144
+
145
+ tree = ast.parse(source_code)
146
+ module_data = self._extract_module_info(tree, file_path, source_code)
147
+ api_data["modules"][str(file_path)] = module_data
148
+
149
+ except Exception as e:
150
+ self.console.print(
151
+ f"[yellow]Warning: Could not parse {file_path}: {e}[/yellow]"
152
+ )
153
+ continue
154
+
155
+ return api_data
156
+
157
+ def extract_protocol_definitions(self, protocol_file: Path) -> dict[str, t.Any]:
158
+ """Extract protocol definitions from protocols.py file."""
159
+ if not protocol_file.exists():
160
+ return {}
161
+
162
+ try:
163
+ source_code = Path(protocol_file).read_text(encoding="utf-8")
164
+
165
+ tree = ast.parse(source_code)
166
+ protocols = {}
167
+
168
+ for node in ast.walk(tree):
169
+ if isinstance(node, ast.ClassDef) and self._is_protocol_class(node):
170
+ protocol_info = self._extract_protocol_info(node, source_code)
171
+ protocols[node.name] = protocol_info
172
+
173
+ return {"protocols": protocols}
174
+
175
+ except Exception as e:
176
+ self.console.print(f"[red]Error extracting protocols: {e}[/red]")
177
+ return {}
178
+
179
+ def extract_service_interfaces(self, service_files: list[Path]) -> dict[str, t.Any]:
180
+ """Extract service interfaces and their methods."""
181
+ services = {}
182
+
183
+ for file_path in service_files:
184
+ if not file_path.exists() or file_path.suffix != ".py":
185
+ continue
186
+
187
+ try:
188
+ source_code = Path(file_path).read_text(encoding="utf-8")
189
+
190
+ tree = ast.parse(source_code)
191
+ service_info = self._extract_service_info(tree, file_path, source_code)
192
+ if service_info:
193
+ services[file_path.stem] = service_info
194
+
195
+ except Exception as e:
196
+ self.console.print(
197
+ f"[yellow]Warning: Could not parse service {file_path}: {e}[/yellow]"
198
+ )
199
+ continue
200
+
201
+ return {"services": services}
202
+
203
+ def extract_cli_commands(self, cli_files: list[Path]) -> dict[str, t.Any]:
204
+ """Extract CLI command definitions and options."""
205
+ cli_data: dict[str, t.Any] = {"commands": {}, "options": {}}
206
+
207
+ for file_path in cli_files:
208
+ if not file_path.exists() or file_path.suffix != ".py":
209
+ continue
210
+
211
+ try:
212
+ source_code = Path(file_path).read_text(encoding="utf-8")
213
+
214
+ tree = ast.parse(source_code)
215
+ cli_info = self._extract_cli_info(tree, source_code)
216
+ cli_data["commands"][file_path.stem] = cli_info
217
+
218
+ except Exception as e:
219
+ self.console.print(
220
+ f"[yellow]Warning: Could not parse CLI file {file_path}: {e}[/yellow]"
221
+ )
222
+ continue
223
+
224
+ return cli_data
225
+
226
+ def extract_mcp_tools(self, mcp_files: list[Path]) -> dict[str, t.Any]:
227
+ """Extract MCP tool definitions and their capabilities."""
228
+ mcp_tools = {}
229
+
230
+ for file_path in mcp_files:
231
+ if not file_path.exists():
232
+ continue
233
+
234
+ try:
235
+ if file_path.suffix == ".py":
236
+ source_code = Path(file_path).read_text(encoding="utf-8")
237
+ tree = ast.parse(source_code)
238
+ tool_info = self._extract_mcp_python_tools(tree, source_code)
239
+ elif file_path.suffix == ".md":
240
+ markdown_content = Path(file_path).read_text(encoding="utf-8")
241
+ tool_info = self._extract_mcp_markdown_docs(markdown_content)
242
+ else:
243
+ continue
244
+
245
+ if tool_info:
246
+ mcp_tools[file_path.stem] = tool_info
247
+
248
+ except Exception as e:
249
+ self.console.print(
250
+ f"[yellow]Warning: Could not parse MCP file {file_path}: {e}[/yellow]"
251
+ )
252
+ continue
253
+
254
+ return {"mcp_tools": mcp_tools}
255
+
256
+ def _extract_module_info(
257
+ self, tree: ast.AST, file_path: Path, source_code: str
258
+ ) -> dict[str, t.Any]:
259
+ """Extract information from a Python module."""
260
+ module_info = self._create_base_module_info(tree, file_path)
261
+ self._populate_module_components(module_info, tree, source_code)
262
+ return module_info
263
+
264
+ def _create_base_module_info(
265
+ self, tree: ast.AST, file_path: Path
266
+ ) -> dict[str, t.Any]:
267
+ """Create the base module information structure."""
268
+ # ast.get_docstring requires Module, ClassDef, FunctionDef, or AsyncFunctionDef
269
+ docstring = (
270
+ ast.get_docstring(tree)
271
+ if isinstance(
272
+ tree, ast.Module | ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef
273
+ )
274
+ else None
275
+ )
276
+ return {
277
+ "path": str(file_path),
278
+ "docstring": docstring,
279
+ "classes": [],
280
+ "functions": [],
281
+ "imports": [],
282
+ }
283
+
284
+ def _populate_module_components(
285
+ self, module_info: dict[str, t.Any], tree: ast.AST, source_code: str
286
+ ) -> None:
287
+ """Populate module components by walking the AST."""
288
+ for node in ast.walk(tree):
289
+ self._process_ast_node(module_info, node, source_code)
290
+
291
+ def _process_ast_node(
292
+ self, module_info: dict[str, t.Any], node: ast.AST, source_code: str
293
+ ) -> None:
294
+ """Process individual AST nodes and extract relevant information."""
295
+ if isinstance(node, ast.ClassDef):
296
+ class_info = self._extract_class_info(node, source_code)
297
+ module_info["classes"].append(class_info)
298
+ elif isinstance(node, ast.FunctionDef):
299
+ func_info = self._extract_function_info(node, source_code)
300
+ module_info["functions"].append(func_info)
301
+ elif isinstance(node, ast.Import | ast.ImportFrom):
302
+ import_info = self._extract_import_info(node)
303
+ module_info["imports"].append(import_info)
304
+
305
+ def _extract_class_info(
306
+ self, node: ast.ClassDef, source_code: str
307
+ ) -> dict[str, t.Any]:
308
+ """Extract information from a class definition."""
309
+ docstring = ast.get_docstring(node)
310
+ parsed_doc = self.docstring_parser.parse_docstring(docstring)
311
+
312
+ class_info = self._build_base_class_info(node, parsed_doc)
313
+ self._extract_class_methods(class_info, node, source_code)
314
+
315
+ return class_info
316
+
317
+ def _build_base_class_info(
318
+ self, node: ast.ClassDef, parsed_doc: dict[str, t.Any]
319
+ ) -> dict[str, t.Any]:
320
+ """Build the base class information structure."""
321
+ return {
322
+ "name": node.name,
323
+ "docstring": parsed_doc,
324
+ "base_classes": [self._get_node_name(base) for base in node.bases],
325
+ "methods": [],
326
+ "properties": [],
327
+ "is_protocol": self._is_protocol_class(node),
328
+ }
329
+
330
+ def _extract_class_methods(
331
+ self, class_info: dict[str, t.Any], node: ast.ClassDef, source_code: str
332
+ ) -> None:
333
+ """Extract method information from class body."""
334
+ for item in node.body:
335
+ if isinstance(item, ast.FunctionDef):
336
+ method_info = self._extract_function_info(item, source_code)
337
+ method_info["is_method"] = True
338
+ method_info["visibility"] = self._determine_method_visibility(item.name)
339
+ class_info["methods"].append(method_info)
340
+
341
+ def _determine_method_visibility(self, method_name: str) -> str:
342
+ """Determine method visibility based on naming convention."""
343
+ if method_name.startswith("_") and not method_name.startswith("__"):
344
+ return "protected"
345
+ elif method_name.startswith("__"):
346
+ return "private"
347
+ return "public"
348
+
349
+ def _extract_function_info(
350
+ self, node: ast.FunctionDef, source_code: str
351
+ ) -> dict[str, t.Any]:
352
+ """Extract information from a function definition."""
353
+ docstring = ast.get_docstring(node)
354
+ parsed_doc = self.docstring_parser.parse_docstring(docstring)
355
+
356
+ # Extract parameter information
357
+ parameters = []
358
+ for arg in node.args.args:
359
+ param_info = {
360
+ "name": arg.arg,
361
+ "annotation": self._get_annotation_string(arg.annotation),
362
+ "description": parsed_doc["parameters"].get(arg.arg, ""),
363
+ }
364
+ parameters.append(param_info)
365
+
366
+ # Extract return annotation
367
+ return_annotation = self._get_annotation_string(node.returns)
368
+
369
+ func_info = {
370
+ "name": node.name,
371
+ "docstring": parsed_doc,
372
+ "parameters": parameters,
373
+ "return_annotation": return_annotation,
374
+ "is_async": isinstance(node, ast.AsyncFunctionDef),
375
+ "decorators": [
376
+ self._get_node_name(decorator) for decorator in node.decorator_list
377
+ ],
378
+ }
379
+
380
+ return func_info
381
+
382
+ def _extract_protocol_info(
383
+ self, node: ast.ClassDef, source_code: str
384
+ ) -> dict[str, t.Any]:
385
+ """Extract detailed information from a protocol definition."""
386
+ docstring = ast.get_docstring(node)
387
+ parsed_doc = self.docstring_parser.parse_docstring(docstring)
388
+
389
+ protocol_info: dict[str, t.Any] = {
390
+ "name": node.name,
391
+ "docstring": parsed_doc,
392
+ "methods": [],
393
+ "runtime_checkable": any(
394
+ self._get_node_name(decorator) == "runtime_checkable"
395
+ for decorator in node.decorator_list
396
+ ),
397
+ }
398
+
399
+ for item in node.body:
400
+ if isinstance(item, ast.FunctionDef):
401
+ method_info = self._extract_function_info(item, source_code)
402
+ method_info["is_abstract"] = True # Protocol methods are abstract
403
+ if isinstance(protocol_info["methods"], list):
404
+ protocol_info["methods"].append(method_info)
405
+
406
+ return protocol_info
407
+
408
+ def _extract_service_info(
409
+ self, tree: ast.AST, file_path: Path, source_code: str
410
+ ) -> dict[str, t.Any] | None:
411
+ """Extract service implementation information."""
412
+ service_info = self._create_service_info_structure(file_path)
413
+ self._populate_service_classes(service_info, tree, source_code)
414
+ return service_info if service_info["classes"] else None
415
+
416
+ def _create_service_info_structure(self, file_path: Path) -> dict[str, t.Any]:
417
+ """Create the base service information structure."""
418
+ return {
419
+ "path": str(file_path),
420
+ "classes": [],
421
+ "functions": [],
422
+ "protocols_implemented": [],
423
+ }
424
+
425
+ def _populate_service_classes(
426
+ self, service_info: dict[str, t.Any], tree: ast.AST, source_code: str
427
+ ) -> None:
428
+ """Populate service classes by walking the AST."""
429
+ for node in ast.walk(tree):
430
+ if isinstance(node, ast.ClassDef):
431
+ self._process_service_class(service_info, node, source_code)
432
+
433
+ def _process_service_class(
434
+ self, service_info: dict[str, t.Any], node: ast.ClassDef, source_code: str
435
+ ) -> None:
436
+ """Process a single service class and extract its information."""
437
+ class_info = self._extract_class_info(node, source_code)
438
+ self._extract_implemented_protocols(service_info, node)
439
+ self._add_class_to_service_info(service_info, class_info)
440
+
441
+ def _extract_implemented_protocols(
442
+ self, service_info: dict[str, t.Any], node: ast.ClassDef
443
+ ) -> None:
444
+ """Extract protocol implementations from class bases."""
445
+ for base in node.bases:
446
+ base_name = self._get_node_name(base)
447
+ if "Protocol" in base_name and isinstance(
448
+ service_info["protocols_implemented"], list
449
+ ):
450
+ service_info["protocols_implemented"].append(base_name)
451
+
452
+ def _add_class_to_service_info(
453
+ self, service_info: dict[str, t.Any], class_info: dict[str, t.Any]
454
+ ) -> None:
455
+ """Add class information to the service info structure."""
456
+ if isinstance(service_info["classes"], list):
457
+ service_info["classes"].append(class_info)
458
+
459
+ def _extract_cli_info(self, tree: ast.AST, source_code: str) -> dict[str, t.Any]:
460
+ """Extract CLI command and option information."""
461
+ cli_info: dict[str, t.Any] = {"options": [], "commands": [], "arguments": []}
462
+
463
+ # Look for Pydantic model fields that represent CLI options
464
+ for node in ast.walk(tree):
465
+ if isinstance(node, ast.ClassDef):
466
+ self._extract_class_cli_options(node, cli_info)
467
+
468
+ return cli_info
469
+
470
+ def _extract_class_cli_options(
471
+ self, class_node: ast.ClassDef, cli_info: dict[str, t.Any]
472
+ ) -> None:
473
+ """Extract CLI options from a class definition."""
474
+ for item in class_node.body:
475
+ if self._is_cli_field(item) and isinstance(item, ast.AnnAssign):
476
+ field_info = self._create_cli_field_info(item)
477
+ if field_info and isinstance(cli_info["options"], list):
478
+ cli_info["options"].append(field_info)
479
+
480
+ def _is_cli_field(self, item: ast.stmt) -> bool:
481
+ """Check if an AST item represents a CLI field."""
482
+ return isinstance(item, ast.AnnAssign) and item.target is not None
483
+
484
+ def _create_cli_field_info(self, item: ast.AnnAssign) -> dict[str, t.Any] | None:
485
+ """Create field information from an annotated assignment."""
486
+ field_name = getattr(item.target, "id", None)
487
+ if not field_name:
488
+ return None
489
+
490
+ return {
491
+ "name": field_name,
492
+ "type": self._get_annotation_string(item.annotation),
493
+ "description": "",
494
+ }
495
+
496
+ def _extract_mcp_python_tools(
497
+ self, tree: ast.AST, source_code: str
498
+ ) -> dict[str, t.Any] | None:
499
+ """Extract MCP tool information from Python files."""
500
+ tools = []
501
+
502
+ for node in ast.walk(tree):
503
+ if isinstance(node, ast.FunctionDef):
504
+ # Look for functions that might be MCP tools
505
+ func_info = self._extract_function_info(node, source_code)
506
+ if "mcp" in func_info["name"].lower() or any(
507
+ "tool" in dec.lower() for dec in func_info["decorators"]
508
+ ):
509
+ tools.append(func_info)
510
+
511
+ return {"tools": tools} if tools else None
512
+
513
+ def _extract_mcp_markdown_docs(self, content: str) -> dict[str, t.Any]:
514
+ """Extract MCP tool documentation from markdown files."""
515
+ # Simple extraction of sections and command examples
516
+ sections: list[dict[str, t.Any]] = []
517
+ current_section: dict[str, t.Any] | None = None
518
+
519
+ for line in content.split("\n"):
520
+ if line.startswith("#"):
521
+ if current_section:
522
+ sections.append(current_section)
523
+ current_section = {"title": line.strip("#").strip(), "content": []}
524
+ elif current_section:
525
+ current_section["content"].append(line)
526
+
527
+ if current_section:
528
+ sections.append(current_section)
529
+
530
+ return {"sections": sections}
531
+
532
+ def _is_protocol_class(self, node: ast.ClassDef) -> bool:
533
+ """Check if a class is a Protocol definition."""
534
+ return "Protocol" in [self._get_node_name(base) for base in node.bases] or any(
535
+ self._get_node_name(decorator) == "runtime_checkable"
536
+ for decorator in node.decorator_list
537
+ )
538
+
539
+ def _get_node_name(self, node: ast.AST) -> str:
540
+ """Get the name from an AST node."""
541
+ if isinstance(node, ast.Name):
542
+ return node.id
543
+ elif isinstance(node, ast.Attribute):
544
+ return f"{self._get_node_name(node.value)}.{node.attr}"
545
+ elif isinstance(node, ast.Constant):
546
+ return str(node.value)
547
+ return ""
548
+
549
+ def _get_annotation_string(self, annotation: ast.AST | None) -> str:
550
+ """Convert an annotation AST node to a string representation."""
551
+ if annotation is None:
552
+ return ""
553
+
554
+ try:
555
+ return self._process_annotation_node(annotation)
556
+ except Exception:
557
+ return "Any"
558
+
559
+ def _process_annotation_node(self, annotation: ast.AST) -> str:
560
+ """Process different types of annotation nodes."""
561
+ if isinstance(annotation, ast.Name):
562
+ return annotation.id
563
+ elif isinstance(annotation, ast.Attribute):
564
+ return self._process_attribute_annotation(annotation)
565
+ elif isinstance(annotation, ast.Subscript):
566
+ return self._process_subscript_annotation(annotation)
567
+ elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
568
+ return self._process_union_annotation(annotation)
569
+ elif isinstance(annotation, ast.Constant):
570
+ return str(annotation.value)
571
+ elif isinstance(annotation, ast.Tuple):
572
+ return self._process_tuple_annotation(annotation)
573
+ return self._get_fallback_annotation(annotation)
574
+
575
+ def _process_attribute_annotation(self, annotation: ast.Attribute) -> str:
576
+ """Process attribute-based annotation nodes."""
577
+ return f"{self._get_node_name(annotation.value)}.{annotation.attr}"
578
+
579
+ def _process_subscript_annotation(self, annotation: ast.Subscript) -> str:
580
+ """Process subscript-based annotation nodes (e.g., List[str])."""
581
+ value = self._get_node_name(annotation.value)
582
+ slice_val = self._get_annotation_string(annotation.slice)
583
+ return f"{value}[{slice_val}]"
584
+
585
+ def _process_union_annotation(self, annotation: ast.BinOp) -> str:
586
+ """Process Union types with | operator (Python 3.10+)."""
587
+ left = self._get_annotation_string(annotation.left)
588
+ right = self._get_annotation_string(annotation.right)
589
+ return f"{left} | {right}"
590
+
591
+ def _process_tuple_annotation(self, annotation: ast.Tuple) -> str:
592
+ """Process tuple-based annotation nodes."""
593
+ elements = [self._get_annotation_string(elt) for elt in annotation.elts]
594
+ return f"({', '.join(elements)})"
595
+
596
+ def _get_fallback_annotation(self, annotation: ast.AST) -> str:
597
+ """Get fallback annotation representation."""
598
+ return ast.unparse(annotation) if hasattr(ast, "unparse") else "Any"
599
+
600
+ def _extract_import_info(
601
+ self, node: ast.Import | ast.ImportFrom
602
+ ) -> dict[str, t.Any]:
603
+ """Extract import statement information."""
604
+ if isinstance(node, ast.Import):
605
+ return {
606
+ "type": "import",
607
+ "names": [alias.name for alias in node.names],
608
+ "from": None,
609
+ }
610
+
611
+ # ast.ImportFrom
612
+ return {
613
+ "type": "from_import",
614
+ "from": node.module,
615
+ "names": [alias.name for alias in node.names] if node.names else ["*"],
616
+ "level": node.level,
617
+ }