crackerjack 0.33.0__py3-none-any.whl → 0.33.1__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.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (198) hide show
  1. crackerjack/__main__.py +1350 -34
  2. crackerjack/adapters/__init__.py +17 -0
  3. crackerjack/adapters/lsp_client.py +358 -0
  4. crackerjack/adapters/rust_tool_adapter.py +194 -0
  5. crackerjack/adapters/rust_tool_manager.py +193 -0
  6. crackerjack/adapters/skylos_adapter.py +231 -0
  7. crackerjack/adapters/zuban_adapter.py +560 -0
  8. crackerjack/agents/base.py +7 -3
  9. crackerjack/agents/coordinator.py +271 -33
  10. crackerjack/agents/documentation_agent.py +9 -15
  11. crackerjack/agents/dry_agent.py +3 -15
  12. crackerjack/agents/formatting_agent.py +1 -1
  13. crackerjack/agents/import_optimization_agent.py +36 -180
  14. crackerjack/agents/performance_agent.py +17 -98
  15. crackerjack/agents/performance_helpers.py +7 -31
  16. crackerjack/agents/proactive_agent.py +1 -3
  17. crackerjack/agents/refactoring_agent.py +16 -85
  18. crackerjack/agents/refactoring_helpers.py +7 -42
  19. crackerjack/agents/security_agent.py +9 -48
  20. crackerjack/agents/test_creation_agent.py +356 -513
  21. crackerjack/agents/test_specialist_agent.py +0 -4
  22. crackerjack/api.py +6 -25
  23. crackerjack/cli/cache_handlers.py +204 -0
  24. crackerjack/cli/cache_handlers_enhanced.py +683 -0
  25. crackerjack/cli/facade.py +100 -0
  26. crackerjack/cli/handlers.py +224 -9
  27. crackerjack/cli/interactive.py +6 -4
  28. crackerjack/cli/options.py +642 -55
  29. crackerjack/cli/utils.py +2 -1
  30. crackerjack/code_cleaner.py +58 -117
  31. crackerjack/config/global_lock_config.py +8 -48
  32. crackerjack/config/hooks.py +53 -62
  33. crackerjack/core/async_workflow_orchestrator.py +24 -34
  34. crackerjack/core/autofix_coordinator.py +3 -17
  35. crackerjack/core/enhanced_container.py +4 -13
  36. crackerjack/core/file_lifecycle.py +12 -89
  37. crackerjack/core/performance.py +2 -2
  38. crackerjack/core/performance_monitor.py +15 -55
  39. crackerjack/core/phase_coordinator.py +104 -204
  40. crackerjack/core/resource_manager.py +14 -90
  41. crackerjack/core/service_watchdog.py +62 -95
  42. crackerjack/core/session_coordinator.py +149 -0
  43. crackerjack/core/timeout_manager.py +14 -72
  44. crackerjack/core/websocket_lifecycle.py +13 -78
  45. crackerjack/core/workflow_orchestrator.py +171 -174
  46. crackerjack/docs/INDEX.md +11 -0
  47. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  48. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  49. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  50. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  51. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  52. crackerjack/documentation/__init__.py +31 -0
  53. crackerjack/documentation/ai_templates.py +756 -0
  54. crackerjack/documentation/dual_output_generator.py +765 -0
  55. crackerjack/documentation/mkdocs_integration.py +518 -0
  56. crackerjack/documentation/reference_generator.py +977 -0
  57. crackerjack/dynamic_config.py +55 -50
  58. crackerjack/executors/async_hook_executor.py +10 -15
  59. crackerjack/executors/cached_hook_executor.py +117 -43
  60. crackerjack/executors/hook_executor.py +8 -34
  61. crackerjack/executors/hook_lock_manager.py +26 -183
  62. crackerjack/executors/individual_hook_executor.py +13 -11
  63. crackerjack/executors/lsp_aware_hook_executor.py +270 -0
  64. crackerjack/executors/tool_proxy.py +417 -0
  65. crackerjack/hooks/lsp_hook.py +79 -0
  66. crackerjack/intelligence/adaptive_learning.py +25 -10
  67. crackerjack/intelligence/agent_orchestrator.py +2 -5
  68. crackerjack/intelligence/agent_registry.py +34 -24
  69. crackerjack/intelligence/agent_selector.py +5 -7
  70. crackerjack/interactive.py +17 -6
  71. crackerjack/managers/async_hook_manager.py +0 -1
  72. crackerjack/managers/hook_manager.py +79 -1
  73. crackerjack/managers/publish_manager.py +44 -8
  74. crackerjack/managers/test_command_builder.py +1 -15
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +98 -7
  77. crackerjack/managers/test_manager_backup.py +10 -9
  78. crackerjack/mcp/cache.py +2 -2
  79. crackerjack/mcp/client_runner.py +1 -1
  80. crackerjack/mcp/context.py +191 -68
  81. crackerjack/mcp/dashboard.py +7 -5
  82. crackerjack/mcp/enhanced_progress_monitor.py +31 -28
  83. crackerjack/mcp/file_monitor.py +30 -23
  84. crackerjack/mcp/progress_components.py +31 -21
  85. crackerjack/mcp/progress_monitor.py +50 -53
  86. crackerjack/mcp/rate_limiter.py +6 -6
  87. crackerjack/mcp/server_core.py +17 -16
  88. crackerjack/mcp/service_watchdog.py +2 -1
  89. crackerjack/mcp/state.py +4 -7
  90. crackerjack/mcp/task_manager.py +11 -9
  91. crackerjack/mcp/tools/core_tools.py +173 -32
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +8 -10
  94. crackerjack/mcp/tools/execution_tools_backup.py +42 -30
  95. crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
  96. crackerjack/mcp/tools/intelligence_tools.py +5 -2
  97. crackerjack/mcp/tools/monitoring_tools.py +33 -70
  98. crackerjack/mcp/tools/proactive_tools.py +24 -11
  99. crackerjack/mcp/tools/progress_tools.py +5 -8
  100. crackerjack/mcp/tools/utility_tools.py +20 -14
  101. crackerjack/mcp/tools/workflow_executor.py +62 -40
  102. crackerjack/mcp/websocket/app.py +8 -0
  103. crackerjack/mcp/websocket/endpoints.py +352 -357
  104. crackerjack/mcp/websocket/jobs.py +40 -57
  105. crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
  106. crackerjack/mcp/websocket/server.py +7 -25
  107. crackerjack/mcp/websocket/websocket_handler.py +6 -17
  108. crackerjack/mixins/__init__.py +0 -2
  109. crackerjack/mixins/error_handling.py +1 -70
  110. crackerjack/models/config.py +12 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +122 -122
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  115. crackerjack/monitoring/metrics_collector.py +426 -0
  116. crackerjack/monitoring/regression_prevention.py +8 -8
  117. crackerjack/monitoring/websocket_server.py +643 -0
  118. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  119. crackerjack/orchestration/coverage_improvement.py +3 -3
  120. crackerjack/orchestration/execution_strategies.py +26 -6
  121. crackerjack/orchestration/test_progress_streamer.py +8 -5
  122. crackerjack/plugins/base.py +2 -2
  123. crackerjack/plugins/hooks.py +7 -0
  124. crackerjack/plugins/managers.py +11 -8
  125. crackerjack/security/__init__.py +0 -1
  126. crackerjack/security/audit.py +6 -35
  127. crackerjack/services/anomaly_detector.py +392 -0
  128. crackerjack/services/api_extractor.py +615 -0
  129. crackerjack/services/backup_service.py +2 -2
  130. crackerjack/services/bounded_status_operations.py +15 -152
  131. crackerjack/services/cache.py +127 -1
  132. crackerjack/services/changelog_automation.py +395 -0
  133. crackerjack/services/config.py +15 -9
  134. crackerjack/services/config_merge.py +19 -80
  135. crackerjack/services/config_template.py +506 -0
  136. crackerjack/services/contextual_ai_assistant.py +48 -22
  137. crackerjack/services/coverage_badge_service.py +171 -0
  138. crackerjack/services/coverage_ratchet.py +27 -25
  139. crackerjack/services/debug.py +3 -3
  140. crackerjack/services/dependency_analyzer.py +460 -0
  141. crackerjack/services/dependency_monitor.py +14 -11
  142. crackerjack/services/documentation_generator.py +491 -0
  143. crackerjack/services/documentation_service.py +675 -0
  144. crackerjack/services/enhanced_filesystem.py +6 -5
  145. crackerjack/services/enterprise_optimizer.py +865 -0
  146. crackerjack/services/error_pattern_analyzer.py +676 -0
  147. crackerjack/services/file_hasher.py +1 -1
  148. crackerjack/services/git.py +8 -25
  149. crackerjack/services/health_metrics.py +10 -8
  150. crackerjack/services/heatmap_generator.py +735 -0
  151. crackerjack/services/initialization.py +11 -30
  152. crackerjack/services/input_validator.py +5 -97
  153. crackerjack/services/intelligent_commit.py +327 -0
  154. crackerjack/services/log_manager.py +15 -12
  155. crackerjack/services/logging.py +4 -3
  156. crackerjack/services/lsp_client.py +628 -0
  157. crackerjack/services/memory_optimizer.py +19 -87
  158. crackerjack/services/metrics.py +42 -33
  159. crackerjack/services/parallel_executor.py +9 -67
  160. crackerjack/services/pattern_cache.py +1 -1
  161. crackerjack/services/pattern_detector.py +6 -6
  162. crackerjack/services/performance_benchmarks.py +18 -59
  163. crackerjack/services/performance_cache.py +20 -81
  164. crackerjack/services/performance_monitor.py +27 -95
  165. crackerjack/services/predictive_analytics.py +510 -0
  166. crackerjack/services/quality_baseline.py +234 -0
  167. crackerjack/services/quality_baseline_enhanced.py +646 -0
  168. crackerjack/services/quality_intelligence.py +785 -0
  169. crackerjack/services/regex_patterns.py +605 -524
  170. crackerjack/services/regex_utils.py +43 -123
  171. crackerjack/services/secure_path_utils.py +5 -164
  172. crackerjack/services/secure_status_formatter.py +30 -141
  173. crackerjack/services/secure_subprocess.py +11 -92
  174. crackerjack/services/security.py +9 -41
  175. crackerjack/services/security_logger.py +12 -24
  176. crackerjack/services/server_manager.py +124 -16
  177. crackerjack/services/status_authentication.py +16 -159
  178. crackerjack/services/status_security_manager.py +4 -131
  179. crackerjack/services/thread_safe_status_collector.py +19 -125
  180. crackerjack/services/unified_config.py +21 -13
  181. crackerjack/services/validation_rate_limiter.py +5 -54
  182. crackerjack/services/version_analyzer.py +459 -0
  183. crackerjack/services/version_checker.py +1 -1
  184. crackerjack/services/websocket_resource_limiter.py +10 -144
  185. crackerjack/services/zuban_lsp_service.py +390 -0
  186. crackerjack/slash_commands/__init__.py +2 -7
  187. crackerjack/slash_commands/run.md +2 -2
  188. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  189. crackerjack/tools/validate_regex_patterns.py +19 -48
  190. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.1.dist-info/RECORD +229 -0
  192. crackerjack/CLAUDE.md +0 -207
  193. crackerjack/RULES.md +0 -380
  194. crackerjack/py313.py +0 -234
  195. crackerjack-0.33.0.dist-info/RECORD +0 -187
  196. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,10 +1,3 @@
1
- """
2
- Utility functions for immediate regex pattern testing and validation.
3
-
4
- Provides quick functions for testing regex patterns before adding them
5
- to the centralized registry, and utilities for migrating existing re.sub() calls.
6
- """
7
-
8
1
  import re
9
2
  import typing as t
10
3
  from pathlib import Path
@@ -18,11 +11,6 @@ def test_pattern_immediately(
18
11
  test_cases: list[tuple[str, str]],
19
12
  description: str = "",
20
13
  ) -> dict[str, t.Any]:
21
- """
22
- Test a regex pattern immediately without adding to registry.
23
-
24
- Returns a report of test results for quick validation.
25
- """
26
14
  results: dict[str, t.Any] = {
27
15
  "pattern": pattern,
28
16
  "replacement": replacement,
@@ -33,9 +21,6 @@ def test_pattern_immediately(
33
21
  "errors": [],
34
22
  }
35
23
 
36
- # Check for forbidden replacement syntax first using safe patterns\n forbidden_checks = [\n (r\"\\\\g\\\\s*<\\\\s*\\\\d+\\\\s*>\", \"\\\\g < 1 > with spaces\"),\n (r\"\\\\g<\\\\s+\\\\d+>\", \"\\\\g< 1> with space after <\"),\n (r\"\\\\g<\\\\d+\\\\s+>\", \"\\\\g<1 > with space before >\"),\n ]
37
-
38
- # Validate pattern compilation using safe cache
39
24
  try:
40
25
  compiled = CompiledPatternCache.get_compiled_pattern(pattern)
41
26
  except ValueError as e:
@@ -43,7 +28,6 @@ def test_pattern_immediately(
43
28
  results["all_passed"] = False
44
29
  return results
45
30
 
46
- # Test all cases
47
31
  for i, (input_text, expected) in enumerate(test_cases):
48
32
  try:
49
33
  result = compiled.sub(replacement, input_text)
@@ -71,7 +55,6 @@ def test_pattern_immediately(
71
55
  )
72
56
  results["all_passed"] = False
73
57
 
74
- # Safety warnings
75
58
  if ".*.*" in pattern:
76
59
  results["warnings"].append(
77
60
  "Multiple .* constructs may cause performance issues"
@@ -85,7 +68,6 @@ def test_pattern_immediately(
85
68
 
86
69
 
87
70
  def print_pattern_test_report(results: dict[str, t.Any]) -> None:
88
- """Print a formatted report of pattern test results."""
89
71
  print("\n🔍 REGEX PATTERN TEST REPORT")
90
72
  print("=" * 50)
91
73
  print(f"Pattern: {results['pattern']}")
@@ -95,25 +77,25 @@ def print_pattern_test_report(results: dict[str, t.Any]) -> None:
95
77
  print()
96
78
 
97
79
  if results["errors"]:
98
- print("❌ ERRORS:")
80
+ print("❌ ERRORS: ")
99
81
  for error in results["errors"]:
100
- print(f" • {error}")
82
+ print(f" • {error}")
101
83
  print()
102
84
 
103
85
  if results["warnings"]:
104
- print("⚠️ WARNINGS:")
86
+ print("⚠️ WARNINGS: ")
105
87
  for warning in results["warnings"]:
106
- print(f" • {warning}")
88
+ print(f" • {warning}")
107
89
  print()
108
90
 
109
- print("📋 TEST CASES:")
91
+ print("📋 TEST CASES: ")
110
92
  for test in results["test_results"]:
111
93
  status = "✅ PASS" if test["passed"] else "❌ FAIL"
112
94
  print(
113
- f" {status} Test {test['test_case']}: '{test['input']}' → '{test['actual']}'"
95
+ f" {status} Test {test['test_case']}: '{test['input']}' → '{test['actual']}'"
114
96
  )
115
97
  if not test["passed"]:
116
- print(f" Expected: '{test['expected']}'")
98
+ print(f" Expected: '{test['expected']}'")
117
99
 
118
100
  print(
119
101
  f"\n🎯 OVERALL: {'✅ ALL TESTS PASSED' if results['all_passed'] else '❌ TESTS FAILED'}"
@@ -127,21 +109,19 @@ def quick_pattern_test(
127
109
  test_cases: list[tuple[str, str]],
128
110
  description: str = "",
129
111
  ) -> bool:
130
- """Quick test function that returns True if all tests pass."""
131
112
  results = test_pattern_immediately(pattern, replacement, test_cases, description)
132
113
  print_pattern_test_report(results)
133
- return results["all_passed"]
114
+ passed: bool = results["all_passed"]
115
+ return passed
134
116
 
135
117
 
136
118
  def find_safe_pattern_for_text(text: str) -> list[str]:
137
- """Find which existing safe patterns would match the given text."""
138
119
  matches = []
139
120
  for name, pattern in SAFE_PATTERNS.items():
140
121
  try:
141
122
  if pattern.test(text):
142
123
  matches.append(name)
143
124
  except Exception:
144
- # Skip patterns that error
145
125
  continue
146
126
  return matches
147
127
 
@@ -149,17 +129,6 @@ def find_safe_pattern_for_text(text: str) -> list[str]:
149
129
  def suggest_migration_for_re_sub(
150
130
  original_pattern: str, original_replacement: str, sample_text: str = ""
151
131
  ) -> dict[str, t.Any]:
152
- """
153
- Suggest how to migrate a raw re.sub() call to use safe patterns.
154
-
155
- Args:
156
- original_pattern: The original regex pattern
157
- original_replacement: The original replacement string
158
- sample_text: Optional sample text to test against
159
-
160
- Returns:
161
- Dictionary with migration suggestions
162
- """
163
132
  suggestion: dict[str, t.Any] = {
164
133
  "original_pattern": original_pattern,
165
134
  "original_replacement": original_replacement,
@@ -170,10 +139,9 @@ def suggest_migration_for_re_sub(
170
139
  "test_cases_needed": [],
171
140
  }
172
141
 
173
- # Check for safety issues first using safe patterns
174
142
  forbidden_checks = [
175
- (r"\\g\s*<\s*\d+\s*>", "\\g < 1 > with spaces"),
176
- (r"\\g<\s+\d+>", "\\g< 1> with space after <"),
143
+ (r"\\g\s*<\s*\d+\s*>", "\\g<1> with spaces"),
144
+ (r"\\g<\s+\d+>", "\\g<1> with space after <"),
177
145
  (r"\\\\g<\\d+\\s+>", "\\\\g<1 > with space before >"),
178
146
  ]
179
147
  for forbidden_pattern, _ in forbidden_checks:
@@ -183,14 +151,12 @@ def suggest_migration_for_re_sub(
183
151
  "CRITICAL: Bad replacement syntax - spaces in \\g<1>"
184
152
  )
185
153
 
186
- # Look for existing patterns that might work
187
154
  if sample_text:
188
155
  matches = find_safe_pattern_for_text(sample_text)
189
156
  suggestion["existing_matches"] = matches
190
157
  if matches:
191
158
  suggestion["needs_new_pattern"] = False
192
159
 
193
- # Generate suggested pattern name based on original pattern
194
160
  if "python.*-.*m" in original_pattern:
195
161
  suggestion["suggested_name"] = "fix_python_command_spacing"
196
162
  elif r"\-\s*\-" in original_pattern:
@@ -200,7 +166,6 @@ def suggest_migration_for_re_sub(
200
166
  elif "password" in original_pattern.lower():
201
167
  suggestion["suggested_name"] = "fix_password_pattern"
202
168
  else:
203
- # Generate name from pattern keywords using safe pattern
204
169
  keyword_pattern = CompiledPatternCache.get_compiled_pattern(r"[a-zA-Z]+")
205
170
  keywords = keyword_pattern.findall(original_pattern)
206
171
  if keywords:
@@ -210,17 +175,15 @@ def suggest_migration_for_re_sub(
210
175
  else:
211
176
  suggestion["suggested_name"] = "fix_custom_pattern"
212
177
 
213
- # Suggest test cases based on common patterns
214
178
  if sample_text:
215
179
  suggestion["test_cases_needed"].append((sample_text, "Expected output needed"))
216
180
 
217
- # Common test cases for spacing issues
218
181
  if "-" in original_pattern:
219
182
  suggestion["test_cases_needed"].extend(
220
183
  [
221
184
  ("word - word", "word-word"),
222
- ("already-good", "already-good"), # No change
223
- ("multiple - word - spacing", "multiple-word - spacing"), # Partial fix
185
+ ("already-good", "already-good"),
186
+ ("multiple - word - spacing", "multiple-word - spacing"),
224
187
  ]
225
188
  )
226
189
 
@@ -228,7 +191,6 @@ def suggest_migration_for_re_sub(
228
191
 
229
192
 
230
193
  def print_migration_suggestion(suggestion: dict[str, t.Any]) -> None:
231
- """Print a formatted migration suggestion report."""
232
194
  print("\n🔄 REGEX MIGRATION SUGGESTION")
233
195
  print("=" * 50)
234
196
  print(f"Original Pattern: {suggestion['original_pattern']}")
@@ -236,59 +198,54 @@ def print_migration_suggestion(suggestion: dict[str, t.Any]) -> None:
236
198
  print()
237
199
 
238
200
  if suggestion["safety_issues"]:
239
- print("❌ SAFETY ISSUES:")
201
+ print("❌ SAFETY ISSUES: ")
240
202
  for issue in suggestion["safety_issues"]:
241
- print(f" • {issue}")
203
+ print(f" • {issue}")
242
204
  print()
243
205
 
244
206
  if suggestion["existing_matches"]:
245
- print("✅ EXISTING PATTERNS AVAILABLE:")
207
+ print("✅ EXISTING PATTERNS AVAILABLE: ")
246
208
  for pattern_name in suggestion["existing_matches"]:
247
209
  pattern = SAFE_PATTERNS[pattern_name]
248
- print(f" • {pattern_name}: {pattern.description}")
210
+ print(f" • {pattern_name}: {pattern.description}")
249
211
  print("💡 Consider using existing patterns instead of creating new ones.")
250
212
  print()
251
213
 
252
214
  if suggestion["needs_new_pattern"]:
253
- print("🆕 NEW PATTERN NEEDED:")
254
- print(f" Suggested Name: {suggestion['suggested_name']}")
255
- print(" Add to crackerjack/services/regex_patterns.py:")
215
+ print("🆕 NEW PATTERN NEEDED: ")
216
+ print(f" Suggested Name: {suggestion['suggested_name']}")
217
+ print(" Add to crackerjack/services/regex_patterns.py: ")
256
218
  print()
257
- print(" ```python")
258
- print(f' "{suggestion["suggested_name"]}": ValidatedPattern(')
259
- print(f' name="{suggestion["suggested_name"]}",')
260
- print(f' pattern=r"{suggestion["original_pattern"]}",')
261
- print(f' replacement=r"{suggestion["original_replacement"]}",')
262
- print(' description="TODO: Add description",')
263
- print(" test_cases=[")
219
+ print(" ```python")
220
+ print(f' "{suggestion["suggested_name"]}": ValidatedPattern(')
221
+ print(f' name="{suggestion["suggested_name"]}", ')
222
+ print(f' pattern=r"{suggestion["original_pattern"]}", ')
223
+ print(f' replacement=r"{suggestion["original_replacement"]}", ')
224
+ print(' description="TODO: Add description", ')
225
+ print(" test_cases=[")
264
226
  for test_input, test_output in suggestion["test_cases_needed"]:
265
- print(f' ("{test_input}", "{test_output}"),')
266
- print(" ]")
267
- print(" ),")
268
- print(" ```")
227
+ print(f' ("{test_input}", "{test_output}"), ')
228
+ print(" ]")
229
+ print(" ), ")
230
+ print(" ```")
269
231
  print()
270
232
 
271
- print("🔧 MIGRATION STEPS:")
272
- print(" 1. Fix any safety issues in replacement syntax")
233
+ print("🔧 MIGRATION STEPS: ")
234
+ print(" 1. Fix any safety issues in replacement syntax")
273
235
  if suggestion["existing_matches"]:
274
- print(" 2. Use existing safe patterns if possible:")
236
+ print(" 2. Use existing safe patterns if possible: ")
275
237
  for pattern_name in suggestion["existing_matches"]:
276
- print(f" SAFE_PATTERNS['{pattern_name}'].apply(text)")
238
+ print(f" SAFE_PATTERNS['{pattern_name}'].apply(text)")
277
239
  if suggestion["needs_new_pattern"]:
278
- print(" 3. Add new ValidatedPattern to regex_patterns.py")
279
- print(" 4. Test thoroughly with comprehensive test cases")
280
- print(" 5. Replace re.sub() call with safe pattern usage")
281
- print(" 6. Run pre-commit hook to validate")
240
+ print(" 3. Add new ValidatedPattern to regex_patterns.py")
241
+ print(" 4. Test thoroughly with comprehensive test cases")
242
+ print(" 5. Replace re.sub() call with safe pattern usage")
243
+ print(" 6. Run pre-commit hook to validate")
282
244
 
283
245
  print("=" * 50)
284
246
 
285
247
 
286
248
  def audit_file_for_re_sub(file_path: Path) -> list[dict[str, t.Any]]:
287
- """
288
- Audit a file for re.sub() calls and return migration suggestions.
289
-
290
- Returns list of findings with line numbers and suggestions.
291
- """
292
249
  findings = []
293
250
 
294
251
  try:
@@ -296,9 +253,8 @@ def audit_file_for_re_sub(file_path: Path) -> list[dict[str, t.Any]]:
296
253
  lines = content.split("\n")
297
254
 
298
255
  for i, line in enumerate(lines, 1):
299
- # Look for re.sub() calls using safe pattern
300
256
  re_sub_pattern = CompiledPatternCache.get_compiled_pattern(
301
- r're\.sub\s*\(\s*[r]?["\']([^"\']+)["\'],\s*[r]?["\']([^"\']*)["\']'
257
+ r're\.sub\s*\(\s*[r]?["\']([^"\']+)["\'], \s*[r]?["\']([^"\']*)["\']'
302
258
  )
303
259
  re_sub_match = re_sub_pattern.search(line)
304
260
  if re_sub_match:
@@ -328,18 +284,11 @@ def audit_file_for_re_sub(file_path: Path) -> list[dict[str, t.Any]]:
328
284
 
329
285
 
330
286
  def audit_codebase_re_sub() -> dict[str, list[dict[str, t.Any]]]:
331
- """
332
- Audit entire crackerjack codebase for re.sub() usage.
333
-
334
- Returns dictionary mapping file paths to findings.
335
- """
336
287
  findings_by_file = {}
337
288
 
338
- # Audit crackerjack package
339
289
  crackerjack_dir = Path(__file__).parent.parent
340
290
 
341
291
  for py_file in crackerjack_dir.rglob("*.py"):
342
- # Skip test files and __pycache__
343
292
  if "test_" in py_file.name or "__pycache__" in str(py_file):
344
293
  continue
345
294
 
@@ -351,18 +300,6 @@ def audit_codebase_re_sub() -> dict[str, list[dict[str, t.Any]]]:
351
300
 
352
301
 
353
302
  def replace_unsafe_regex_with_safe_patterns(content: str) -> str:
354
- """
355
- Replace unsafe regex patterns in content with safe alternatives.
356
-
357
- This function looks for common regex patterns and replaces them with
358
- calls to SAFE_PATTERNS where possible.
359
-
360
- Args:
361
- content: The source code content to fix
362
-
363
- Returns:
364
- Fixed content with safe patterns applied
365
- """
366
303
  lines = content.split("\n")
367
304
  modified = False
368
305
 
@@ -371,10 +308,8 @@ def replace_unsafe_regex_with_safe_patterns(content: str) -> str:
371
308
  for i, line in enumerate(lines):
372
309
  original_line = line
373
310
 
374
- # Fix critical replacement syntax issues first
375
311
  line = _fix_replacement_syntax_issues(line)
376
312
 
377
- # Process re.sub patterns
378
313
  line, _, needs_import = _process_re_sub_patterns(line, has_safe_patterns_import)
379
314
 
380
315
  if needs_import and not has_safe_patterns_import:
@@ -384,9 +319,8 @@ def replace_unsafe_regex_with_safe_patterns(content: str) -> str:
384
319
  "from crackerjack.services.regex_patterns import SAFE_PATTERNS",
385
320
  )
386
321
  has_safe_patterns_import = True
387
- i += 1 # Adjust index for inserted line
322
+ i += 1
388
323
 
389
- # Update the line if it changed
390
324
  if line != original_line:
391
325
  lines[i] = line
392
326
  modified = True
@@ -395,7 +329,6 @@ def replace_unsafe_regex_with_safe_patterns(content: str) -> str:
395
329
 
396
330
 
397
331
  def _check_for_safe_patterns_import(lines: list[str]) -> bool:
398
- """Check if SAFE_PATTERNS import is already present."""
399
332
  return any(
400
333
  "from crackerjack.services.regex_patterns import SAFE_PATTERNS" in line
401
334
  or "SAFE_PATTERNS" in line
@@ -404,9 +337,7 @@ def _check_for_safe_patterns_import(lines: list[str]) -> bool:
404
337
 
405
338
 
406
339
  def _fix_replacement_syntax_issues(line: str) -> str:
407
- """Fix critical replacement syntax issues in regex patterns."""
408
340
  if r"\g < " in line or r"\g< " in line or r"\g <" in line:
409
- # Fix spacing in replacement groups using safe pattern
410
341
  spacing_fix_pattern = CompiledPatternCache.get_compiled_pattern(
411
342
  r"\\g\s*<\s*(\d+)\s*>"
412
343
  )
@@ -417,9 +348,8 @@ def _fix_replacement_syntax_issues(line: str) -> str:
417
348
  def _process_re_sub_patterns(
418
349
  line: str, has_safe_patterns_import: bool
419
350
  ) -> tuple[str, bool, bool]:
420
- """Process re.sub patterns and replace with safe alternatives."""
421
351
  re_sub_match = CompiledPatternCache.get_compiled_pattern(
422
- r're\.sub\s*\(\s*r?["\']([^"\']+)["\']\s*,\s*r?["\']([^"\']*)["\']'
352
+ r're\.sub\s*\(\s*r?["\']([^"\']+)["\']\s*, \s*r?["\']([^"\']*)["\']'
423
353
  ).search(line)
424
354
 
425
355
  if not re_sub_match:
@@ -436,8 +366,6 @@ def _process_re_sub_patterns(
436
366
 
437
367
 
438
368
  def _identify_safe_pattern(pattern: str, replacement: str) -> str | None:
439
- """Identify which safe pattern matches the given regex pattern."""
440
- # Common patterns we can automatically replace
441
369
  if pattern == r"(\w+)\s*-\s*(\w+)" and replacement in (
442
370
  r"\1-\2",
443
371
  r"\g<1>-\g<2>",
@@ -453,11 +381,9 @@ def _identify_safe_pattern(pattern: str, replacement: str) -> str | None:
453
381
  def _replace_with_safe_pattern(
454
382
  line: str, re_sub_match: re.Match[str], safe_pattern_name: str
455
383
  ) -> tuple[str, bool, bool]:
456
- """Replace re.sub call with safe pattern call."""
457
384
  before_re_sub = line[: re_sub_match.start()]
458
385
  after_re_sub = line[re_sub_match.end() :]
459
386
 
460
- # Look for assignment pattern: var = re.sub(...)
461
387
  assign_match = CompiledPatternCache.get_compiled_pattern(r"(\w+)\s*=\s*$").search(
462
388
  before_re_sub
463
389
  )
@@ -476,7 +402,6 @@ def _handle_assignment_pattern(
476
402
  after_re_sub: str,
477
403
  safe_pattern_name: str,
478
404
  ) -> tuple[str, bool, bool]:
479
- """Handle assignment pattern replacement."""
480
405
  var_name = assign_match.group(1)
481
406
  text_var = _extract_source_variable(line)
482
407
  new_line = f"{var_name} = SAFE_PATTERNS['{safe_pattern_name}'].apply({text_var})"
@@ -486,7 +411,6 @@ def _handle_assignment_pattern(
486
411
  def _handle_direct_replacement(
487
412
  line: str, re_sub_match: re.Match[str], safe_pattern_name: str
488
413
  ) -> tuple[str, bool, bool]:
489
- """Handle direct replacement of re.sub call."""
490
414
  text_var = _extract_source_variable(line)
491
415
  new_line = line.replace(
492
416
  re_sub_match.group(0),
@@ -496,15 +420,13 @@ def _handle_direct_replacement(
496
420
 
497
421
 
498
422
  def _extract_source_variable(line: str) -> str:
499
- """Extract the source variable from re.sub call."""
500
423
  full_match = CompiledPatternCache.get_compiled_pattern(
501
- r"re\.sub\s*\([^,]+,\s*[^,]+,\s*(\w+)"
424
+ r"re\.sub\s*\([^, ]+, \s*[^, ]+, \s*(\w+)"
502
425
  ).search(line)
503
426
  return full_match.group(1) if full_match else "text"
504
427
 
505
428
 
506
429
  def _find_import_insertion_point(lines: list[str]) -> int:
507
- """Find the right place to insert the import statement."""
508
430
  import_index = 0
509
431
  for j, check_line in enumerate(lines):
510
432
  if check_line.strip().startswith(("import ", "from ")):
@@ -517,7 +439,6 @@ def _find_import_insertion_point(lines: list[str]) -> int:
517
439
 
518
440
 
519
441
  if __name__ == "__main__":
520
- # Example usage for testing patterns
521
442
  test_result = quick_pattern_test(
522
443
  pattern=r"(\w+)\s*-\s*(\w+)",
523
444
  replacement=r"\1-\2",
@@ -529,7 +450,6 @@ if __name__ == "__main__":
529
450
  description="Fix spacing in hyphenated names",
530
451
  )
531
452
 
532
- # Example migration suggestion
533
453
  print("\n" + "=" * 60)
534
454
  migration = suggest_migration_for_re_sub(
535
455
  r"python\s*-\s*m\s+", "python -m ", "python - m crackerjack"