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,615 @@
1
+ """Safe file modification service with comprehensive security checks.
2
+
3
+ This module provides SafeFileModifier, a service for safely modifying files
4
+ with automatic backups, validation, and atomic operations.
5
+
6
+ Security Features:
7
+ - Symlink detection and blocking (both direct and in path chain)
8
+ - Path traversal prevention (must be within project)
9
+ - File size limits (10MB default, configurable)
10
+ - Forbidden file patterns (.env, .git/*, *.key, etc.)
11
+ - Atomic write operations using tempfile
12
+ - Permission preservation
13
+ - Automatic rollback on errors
14
+
15
+ Features:
16
+ - Automatic backup creation with timestamps
17
+ - Diff generation for review
18
+ - Dry-run mode for previewing changes
19
+ - Rollback on errors
20
+ - Validation of file existence and permissions
21
+ """
22
+
23
+ import difflib
24
+ import os
25
+ import shutil
26
+ import tempfile
27
+ import typing as t
28
+ from contextlib import suppress
29
+ from datetime import datetime
30
+ from fnmatch import fnmatch
31
+ from pathlib import Path
32
+
33
+ from loguru import logger
34
+
35
+ from crackerjack.models.protocols import SafeFileModifierProtocol, ServiceProtocol
36
+
37
+
38
+ class SafeFileModifier(SafeFileModifierProtocol, ServiceProtocol):
39
+ """Safely modify files with backups and validation.
40
+
41
+ Features:
42
+ - Automatic backup creation with timestamps
43
+ - Diff generation for review
44
+ - Dry-run mode for previewing changes
45
+ - Rollback on errors
46
+ - Validation of file existence and permissions
47
+ - Atomic file operations to prevent partial writes
48
+ - Symlink protection to prevent following malicious links
49
+ - File size limits to prevent DoS attacks
50
+
51
+ Security:
52
+ - All file operations use atomic writes (write to temp, then rename)
53
+ - Symlinks are detected and blocked (both direct and in path chain)
54
+ - Path traversal attacks are prevented
55
+ - File size limits enforced
56
+ - Forbidden file patterns blocked (.env, .git/*, etc.)
57
+
58
+ Example:
59
+ >>> modifier = SafeFileModifier()
60
+ >>> result = await modifier.apply_fix(
61
+ ... file_path="myfile.py",
62
+ ... fixed_content="print('hello')",
63
+ ... dry_run=False,
64
+ ... )
65
+ >>> if result["success"]:
66
+ ... print(f"Applied fix, backup at: {result['backup_path']}")
67
+ """
68
+
69
+ # Forbidden file patterns for security
70
+ FORBIDDEN_PATTERNS = [
71
+ ".env*", # .env, .env.local, .env.production, .env.test, etc.
72
+ ".git/*",
73
+ "*.key",
74
+ "*.pem",
75
+ "*.crt",
76
+ "*_rsa",
77
+ "*_dsa",
78
+ "*_ed25519",
79
+ "*.p12",
80
+ "*.pfx",
81
+ "id_rsa*",
82
+ "*.secret",
83
+ "secrets.*",
84
+ ".ssh/*",
85
+ ]
86
+
87
+ def __init__(
88
+ self,
89
+ backup_dir: Path | None = None,
90
+ max_file_size: int = 10_485_760, # 10MB default
91
+ ):
92
+ """Initialize SafeFileModifier.
93
+
94
+ Args:
95
+ backup_dir: Directory for backups (default: .backups)
96
+ max_file_size: Maximum file size in bytes (default: 10MB)
97
+ """
98
+ self._backup_dir = backup_dir or Path(".backups")
99
+ self._max_file_size = max_file_size
100
+ self._ensure_backup_dir()
101
+
102
+ def initialize(self) -> None:
103
+ pass
104
+
105
+ def cleanup(self) -> None:
106
+ pass
107
+
108
+ def health_check(self) -> bool:
109
+ return True
110
+
111
+ def shutdown(self) -> None:
112
+ pass
113
+
114
+ def metrics(self) -> dict[str, t.Any]:
115
+ return {}
116
+
117
+ def is_healthy(self) -> bool:
118
+ return True
119
+
120
+ def register_resource(self, resource: t.Any) -> None:
121
+ pass
122
+
123
+ def cleanup_resource(self, resource: t.Any) -> None:
124
+ pass
125
+
126
+ def record_error(self, error: Exception) -> None:
127
+ pass
128
+
129
+ def increment_requests(self) -> None:
130
+ pass
131
+
132
+ def get_custom_metric(self, name: str) -> t.Any:
133
+ return None
134
+
135
+ def set_custom_metric(self, name: str, value: t.Any) -> None:
136
+ pass
137
+
138
+ def _ensure_backup_dir(self) -> None:
139
+ """Create backup directory if it doesn't exist."""
140
+ if not self._backup_dir.exists():
141
+ self._backup_dir.mkdir(parents=True, exist_ok=True)
142
+ logger.debug(f"Created backup directory: {self._backup_dir}")
143
+
144
+ async def apply_fix(
145
+ self,
146
+ file_path: str,
147
+ fixed_content: str,
148
+ dry_run: bool = False,
149
+ create_backup: bool = True,
150
+ ) -> dict[str, t.Any]:
151
+ """Apply code fix with safety checks.
152
+
153
+ This is the main public API for applying fixes to files.
154
+ Performs comprehensive validation, creates backups, and applies
155
+ changes atomically.
156
+
157
+ Args:
158
+ file_path: Path to file to modify
159
+ fixed_content: New content to write
160
+ dry_run: If True, only generate diff without modifying
161
+ create_backup: If True, create backup before modifying
162
+
163
+ Returns:
164
+ Dictionary with keys:
165
+ - success: bool - Whether operation succeeded
166
+ - diff: str - Unified diff of changes
167
+ - backup_path: str | None - Path to backup file
168
+ - dry_run: bool - Whether this was a dry run
169
+ - message: str - Human-readable message
170
+ - error: str - Error message if failed
171
+
172
+ Example:
173
+ >>> result = await modifier.apply_fix(
174
+ ... "test.py", "print('fixed')", dry_run=True
175
+ ... )
176
+ >>> print(result["diff"])
177
+ """
178
+ return await self._apply_fix(file_path, fixed_content, dry_run, create_backup)
179
+
180
+ async def _apply_fix(
181
+ self,
182
+ file_path: str,
183
+ fixed_content: str,
184
+ dry_run: bool,
185
+ create_backup: bool,
186
+ ) -> dict[str, t.Any]:
187
+ """Internal implementation of fix application with atomic writes.
188
+
189
+ Security features:
190
+ 1. Validates file path (symlinks, traversal, size)
191
+ 2. Validates content size
192
+ 3. Creates backup before modification
193
+ 4. Uses atomic write (temp file + rename)
194
+ 5. Preserves permissions
195
+ 6. Rollback on errors
196
+
197
+ Args:
198
+ file_path: Path to file to modify
199
+ fixed_content: New content to write
200
+ dry_run: If True, only generate diff
201
+ create_backup: If True, create backup
202
+
203
+ Returns:
204
+ Result dictionary with success status and details
205
+ """
206
+ path = Path(file_path)
207
+
208
+ # Validation
209
+ result: dict[str, t.Any] = self._validate_fix_inputs(path, fixed_content)
210
+ if not result["success"]:
211
+ return result
212
+
213
+ # Read original content
214
+ result = self._read_original_content(path)
215
+ if not result["success"]:
216
+ return result
217
+ original_content = result["content"]
218
+
219
+ # Generate diff
220
+ # Type assertion: content is str when success is True
221
+ assert isinstance(original_content, str)
222
+ diff = self._generate_diff(original_content, fixed_content, file_path)
223
+
224
+ # Dry-run mode - just return diff
225
+ if dry_run:
226
+ return self._create_dry_run_result(diff)
227
+
228
+ # Create backup if requested
229
+ # Type assertion: content is str when success is True
230
+ assert isinstance(original_content, str)
231
+ result = self._handle_backup(path, original_content, create_backup, diff)
232
+ if not result["success"]:
233
+ return result
234
+ backup_path = result.get("backup_path")
235
+ # Type assertion: backup_path is Path | None after successful backup creation
236
+ assert backup_path is None or isinstance(backup_path, Path)
237
+
238
+ # Apply the fix atomically
239
+ return self._atomic_write_fix(path, fixed_content, diff, backup_path, file_path)
240
+
241
+ def _validate_fix_inputs(
242
+ self, path: Path, fixed_content: str
243
+ ) -> dict[str, str | bool | None]:
244
+ """Validate file path and content size."""
245
+ validation_result = self._validate_file_path(path)
246
+ if not validation_result["valid"]:
247
+ return {
248
+ "success": False,
249
+ "error": validation_result["error"],
250
+ "diff": "",
251
+ "backup_path": None,
252
+ }
253
+
254
+ if len(fixed_content) > self._max_file_size:
255
+ return {
256
+ "success": False,
257
+ "error": f"Content size {len(fixed_content)} exceeds limit {self._max_file_size}",
258
+ "diff": "",
259
+ "backup_path": None,
260
+ }
261
+
262
+ return {"success": True}
263
+
264
+ def _read_original_content(self, path: Path) -> dict[str, str | bool | None]:
265
+ """Read original file content with error handling."""
266
+ try:
267
+ original_content = path.read_text(encoding="utf-8")
268
+ return {"success": True, "content": original_content}
269
+ except UnicodeDecodeError:
270
+ return {
271
+ "success": False,
272
+ "error": f"File is not valid UTF-8: {path}",
273
+ "diff": "",
274
+ "backup_path": None,
275
+ }
276
+ except Exception as e:
277
+ return {
278
+ "success": False,
279
+ "error": f"Failed to read file: {e}",
280
+ "diff": "",
281
+ "backup_path": None,
282
+ }
283
+
284
+ def _create_dry_run_result(self, diff: str) -> dict[str, str | bool | None]:
285
+ """Create result dictionary for dry-run mode."""
286
+ return {
287
+ "success": True,
288
+ "diff": diff,
289
+ "backup_path": None,
290
+ "dry_run": True,
291
+ "message": "Dry-run: Changes not applied",
292
+ }
293
+
294
+ def _handle_backup(
295
+ self, path: Path, original_content: str, create_backup: bool, diff: str
296
+ ) -> dict[str, str | bool | Path | None]:
297
+ """Create backup if requested."""
298
+ if not create_backup:
299
+ return {"success": True, "backup_path": None}
300
+
301
+ try:
302
+ backup_path = self._create_backup(path, original_content)
303
+ return {"success": True, "backup_path": backup_path}
304
+ except Exception as e:
305
+ logger.error(f"Failed to create backup: {e}")
306
+ return {
307
+ "success": False,
308
+ "error": f"Backup creation failed: {e}",
309
+ "diff": diff,
310
+ "backup_path": None,
311
+ }
312
+
313
+ def _atomic_write_fix(
314
+ self,
315
+ path: Path,
316
+ fixed_content: str,
317
+ diff: str,
318
+ backup_path: Path | None,
319
+ file_path: str,
320
+ ) -> dict[str, str | bool | None]:
321
+ """Write fix atomically with rollback on error."""
322
+ try:
323
+ temp_fd, temp_path_str = tempfile.mkstemp(
324
+ dir=path.parent,
325
+ prefix=f".{path.name}.",
326
+ suffix=".tmp",
327
+ )
328
+
329
+ try:
330
+ with os.fdopen(temp_fd, "w", encoding="utf-8") as f:
331
+ f.write(fixed_content)
332
+ f.flush()
333
+ os.fsync(f.fileno())
334
+
335
+ original_stat = path.stat()
336
+ os.chmod(temp_path_str, original_stat.st_mode)
337
+ shutil.move(temp_path_str, path)
338
+
339
+ logger.info(f"Successfully applied fix to {file_path}")
340
+
341
+ return {
342
+ "success": True,
343
+ "diff": diff,
344
+ "backup_path": str(backup_path) if backup_path else None,
345
+ "dry_run": False,
346
+ "message": f"Fix applied successfully to {file_path}",
347
+ }
348
+
349
+ except Exception:
350
+ with suppress(Exception):
351
+ Path(temp_path_str).unlink()
352
+ raise
353
+
354
+ except Exception as e:
355
+ if backup_path:
356
+ logger.warning(f"Fix failed, restoring from backup: {e}")
357
+ try:
358
+ self._restore_backup(path, backup_path)
359
+ except Exception as restore_error:
360
+ logger.error(f"Rollback failed: {restore_error}")
361
+ return {
362
+ "success": False,
363
+ "error": f"Failed to write file AND rollback failed: {e} (rollback: {restore_error})",
364
+ "diff": diff,
365
+ "backup_path": str(backup_path) if backup_path else None,
366
+ }
367
+
368
+ return {
369
+ "success": False,
370
+ "error": f"Failed to write file: {e}",
371
+ "diff": diff,
372
+ "backup_path": str(backup_path) if backup_path else None,
373
+ }
374
+
375
+ def _check_file_exists(self, path: Path) -> dict[str, bool | str]:
376
+ """Check if the file exists."""
377
+ if not path.exists():
378
+ return {
379
+ "valid": False,
380
+ "error": f"File does not exist: {path}",
381
+ }
382
+ return {"valid": True, "error": ""}
383
+
384
+ def _check_is_symlink(self, path: Path) -> dict[str, bool | str]:
385
+ """Check if the file is a symlink."""
386
+ if path.is_symlink():
387
+ return {
388
+ "valid": False,
389
+ "error": f"Symlinks are not allowed for security reasons: {path}",
390
+ }
391
+ return {"valid": True, "error": ""}
392
+
393
+ def _check_is_file(self, path: Path) -> dict[str, bool | str]:
394
+ """Check if the path is a file."""
395
+ if not path.is_file():
396
+ return {
397
+ "valid": False,
398
+ "error": f"Path is not a file: {path}",
399
+ }
400
+ return {"valid": True, "error": ""}
401
+
402
+ def _check_forbidden_patterns(self, path: Path) -> dict[str, bool | str]:
403
+ """Check if the file matches forbidden patterns."""
404
+ file_str = str(path)
405
+ for pattern in self.FORBIDDEN_PATTERNS:
406
+ if fnmatch(file_str, pattern) or fnmatch(path.name, pattern):
407
+ return {
408
+ "valid": False,
409
+ "error": f"File matches forbidden pattern '{pattern}': {path}",
410
+ }
411
+ return {"valid": True, "error": ""}
412
+
413
+ def _check_file_size(self, path: Path) -> dict[str, bool | str]:
414
+ """Check the file size against the limit."""
415
+ try:
416
+ file_size = path.stat().st_size
417
+ if file_size > self._max_file_size:
418
+ return {
419
+ "valid": False,
420
+ "error": f"File size {file_size} exceeds limit {self._max_file_size}",
421
+ }
422
+ except Exception as e:
423
+ return {
424
+ "valid": False,
425
+ "error": f"Failed to check file size: {e}",
426
+ }
427
+ return {"valid": True, "error": ""}
428
+
429
+ def _check_file_writable(self, path: Path) -> dict[str, bool | str]:
430
+ """Check if the file is writable."""
431
+ if not os.access(path, os.W_OK):
432
+ return {
433
+ "valid": False,
434
+ "error": f"File is not writable: {path}",
435
+ }
436
+ return {"valid": True, "error": ""}
437
+
438
+ def _check_path_traversal(self, path: Path) -> dict[str, bool | str]:
439
+ """Check for path traversal attacks."""
440
+ try:
441
+ resolved_path = path.resolve()
442
+ project_root = Path.cwd().resolve()
443
+
444
+ # Ensure the resolved path is within the project directory
445
+ resolved_path.relative_to(project_root)
446
+
447
+ except ValueError:
448
+ return {
449
+ "valid": False,
450
+ "error": f"File path outside project directory: {path}",
451
+ }
452
+ return {"valid": True, "error": ""}
453
+
454
+ def _check_symlinks_in_path_chain(self, path: Path) -> dict[str, bool | str]:
455
+ """Check for symlinks in the path chain."""
456
+ current = path
457
+ while current != current.parent:
458
+ if current.is_symlink():
459
+ return {
460
+ "valid": False,
461
+ "error": f"Symlink in path chain not allowed: {current}",
462
+ }
463
+ current = current.parent
464
+ return {"valid": True, "error": ""}
465
+
466
+ def _validate_file_path(self, path: Path) -> dict[str, bool | str]:
467
+ """Validate file path before modification with comprehensive security checks.
468
+
469
+ Security checks:
470
+ 1. File existence and type validation
471
+ 2. Symlink detection (blocks symlinks to prevent malicious redirects)
472
+ 3. Path traversal prevention (must be within project)
473
+ 4. File size limits
474
+ 5. Permission checks
475
+ 6. Forbidden file pattern checks
476
+ 7. Path chain symlink validation
477
+
478
+ Args:
479
+ path: Path to validate
480
+
481
+ Returns:
482
+ Dictionary with:
483
+ - valid: bool - Whether path is valid
484
+ - error: str - Error message if invalid
485
+
486
+ Example:
487
+ >>> result = modifier._validate_file_path(Path("test.py"))
488
+ >>> if not result["valid"]:
489
+ ... print(result["error"])
490
+ """
491
+ # Must exist
492
+ result = self._check_file_exists(path)
493
+ if not result["valid"]:
494
+ return result
495
+
496
+ # SECURITY: Block symlinks to prevent following malicious links
497
+ result = self._check_is_symlink(path)
498
+ if not result["valid"]:
499
+ return result
500
+
501
+ # Must be a file (not directory)
502
+ result = self._check_is_file(path)
503
+ if not result["valid"]:
504
+ return result
505
+
506
+ # SECURITY: Check forbidden file patterns
507
+ result = self._check_forbidden_patterns(path)
508
+ if not result["valid"]:
509
+ return result
510
+
511
+ # SECURITY: Check file size before processing
512
+ result = self._check_file_size(path)
513
+ if not result["valid"]:
514
+ return result
515
+
516
+ # Must be writable
517
+ result = self._check_file_writable(path)
518
+ if not result["valid"]:
519
+ return result
520
+
521
+ # SECURITY: Prevent path traversal attacks
522
+ result = self._check_path_traversal(path)
523
+ if not result["valid"]:
524
+ return result
525
+
526
+ # SECURITY: Additional check - ensure no symlinks in the path chain
527
+ return self._check_symlinks_in_path_chain(path)
528
+
529
+ def _create_backup(
530
+ self,
531
+ file_path: Path,
532
+ content: str,
533
+ ) -> Path:
534
+ """Create timestamped backup file.
535
+
536
+ Backup naming: .backups/<filename>_<timestamp>.bak
537
+ Example: .backups/myfile.py_20250103_143022.bak
538
+
539
+ Args:
540
+ file_path: Original file path
541
+ content: Content to backup
542
+
543
+ Returns:
544
+ Path to backup file
545
+
546
+ Raises:
547
+ IOError: If backup creation fails
548
+ """
549
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
550
+ backup_name = f"{file_path.name}_{timestamp}.bak"
551
+ backup_path = self._backup_dir / backup_name
552
+
553
+ # Write backup
554
+ backup_path.write_text(content, encoding="utf-8")
555
+
556
+ logger.debug(f"Created backup: {backup_path}")
557
+
558
+ return backup_path
559
+
560
+ def _restore_backup(
561
+ self,
562
+ file_path: Path,
563
+ backup_path: Path,
564
+ ) -> None:
565
+ """Restore file from backup.
566
+
567
+ Args:
568
+ file_path: File to restore
569
+ backup_path: Backup file to restore from
570
+
571
+ Raises:
572
+ IOError: If restoration fails
573
+ """
574
+ try:
575
+ backup_content = backup_path.read_text(encoding="utf-8")
576
+ file_path.write_text(backup_content, encoding="utf-8")
577
+
578
+ logger.info(f"Restored {file_path} from backup")
579
+
580
+ except Exception as e:
581
+ logger.error(f"Failed to restore backup: {e}")
582
+ raise
583
+
584
+ def _generate_diff(
585
+ self,
586
+ original: str,
587
+ fixed: str,
588
+ filename: str,
589
+ ) -> str:
590
+ """Generate unified diff for review.
591
+
592
+ Args:
593
+ original: Original file content
594
+ fixed: Fixed file content
595
+ filename: Name for diff headers
596
+
597
+ Returns:
598
+ Unified diff string
599
+
600
+ Example:
601
+ >>> diff = modifier._generate_diff("old content", "new content", "test.py")
602
+ >>> print(diff)
603
+ """
604
+ original_lines = original.splitlines(keepends=True)
605
+ fixed_lines = fixed.splitlines(keepends=True)
606
+
607
+ diff = difflib.unified_diff(
608
+ original_lines,
609
+ fixed_lines,
610
+ fromfile=f"{filename} (original)",
611
+ tofile=f"{filename} (fixed)",
612
+ lineterm="",
613
+ )
614
+
615
+ return "".join(diff)