crackerjack 0.32.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 (200) 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 +64 -6
  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 +257 -218
  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 +558 -240
  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 +66 -13
  74. crackerjack/managers/test_command_builder.py +5 -17
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +109 -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 +161 -32
  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 +174 -33
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +15 -12
  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 +3 -0
  109. crackerjack/mixins/error_handling.py +145 -0
  110. crackerjack/models/config.py +21 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +176 -107
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/models/task.py +3 -0
  115. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  116. crackerjack/monitoring/metrics_collector.py +426 -0
  117. crackerjack/monitoring/regression_prevention.py +8 -8
  118. crackerjack/monitoring/websocket_server.py +643 -0
  119. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  120. crackerjack/orchestration/coverage_improvement.py +3 -3
  121. crackerjack/orchestration/execution_strategies.py +26 -6
  122. crackerjack/orchestration/test_progress_streamer.py +8 -5
  123. crackerjack/plugins/base.py +2 -2
  124. crackerjack/plugins/hooks.py +7 -0
  125. crackerjack/plugins/managers.py +11 -8
  126. crackerjack/security/__init__.py +0 -1
  127. crackerjack/security/audit.py +90 -105
  128. crackerjack/services/anomaly_detector.py +392 -0
  129. crackerjack/services/api_extractor.py +615 -0
  130. crackerjack/services/backup_service.py +2 -2
  131. crackerjack/services/bounded_status_operations.py +15 -152
  132. crackerjack/services/cache.py +127 -1
  133. crackerjack/services/changelog_automation.py +395 -0
  134. crackerjack/services/config.py +18 -11
  135. crackerjack/services/config_merge.py +30 -85
  136. crackerjack/services/config_template.py +506 -0
  137. crackerjack/services/contextual_ai_assistant.py +48 -22
  138. crackerjack/services/coverage_badge_service.py +171 -0
  139. crackerjack/services/coverage_ratchet.py +41 -17
  140. crackerjack/services/debug.py +3 -3
  141. crackerjack/services/dependency_analyzer.py +460 -0
  142. crackerjack/services/dependency_monitor.py +14 -11
  143. crackerjack/services/documentation_generator.py +491 -0
  144. crackerjack/services/documentation_service.py +675 -0
  145. crackerjack/services/enhanced_filesystem.py +6 -5
  146. crackerjack/services/enterprise_optimizer.py +865 -0
  147. crackerjack/services/error_pattern_analyzer.py +676 -0
  148. crackerjack/services/file_hasher.py +1 -1
  149. crackerjack/services/git.py +41 -45
  150. crackerjack/services/health_metrics.py +10 -8
  151. crackerjack/services/heatmap_generator.py +735 -0
  152. crackerjack/services/initialization.py +30 -33
  153. crackerjack/services/input_validator.py +5 -97
  154. crackerjack/services/intelligent_commit.py +327 -0
  155. crackerjack/services/log_manager.py +15 -12
  156. crackerjack/services/logging.py +4 -3
  157. crackerjack/services/lsp_client.py +628 -0
  158. crackerjack/services/memory_optimizer.py +409 -0
  159. crackerjack/services/metrics.py +42 -33
  160. crackerjack/services/parallel_executor.py +416 -0
  161. crackerjack/services/pattern_cache.py +1 -1
  162. crackerjack/services/pattern_detector.py +6 -6
  163. crackerjack/services/performance_benchmarks.py +250 -576
  164. crackerjack/services/performance_cache.py +382 -0
  165. crackerjack/services/performance_monitor.py +565 -0
  166. crackerjack/services/predictive_analytics.py +510 -0
  167. crackerjack/services/quality_baseline.py +234 -0
  168. crackerjack/services/quality_baseline_enhanced.py +646 -0
  169. crackerjack/services/quality_intelligence.py +785 -0
  170. crackerjack/services/regex_patterns.py +605 -524
  171. crackerjack/services/regex_utils.py +43 -123
  172. crackerjack/services/secure_path_utils.py +5 -164
  173. crackerjack/services/secure_status_formatter.py +30 -141
  174. crackerjack/services/secure_subprocess.py +11 -92
  175. crackerjack/services/security.py +61 -30
  176. crackerjack/services/security_logger.py +18 -22
  177. crackerjack/services/server_manager.py +124 -16
  178. crackerjack/services/status_authentication.py +16 -159
  179. crackerjack/services/status_security_manager.py +4 -131
  180. crackerjack/services/terminal_utils.py +0 -0
  181. crackerjack/services/thread_safe_status_collector.py +19 -125
  182. crackerjack/services/unified_config.py +21 -13
  183. crackerjack/services/validation_rate_limiter.py +5 -54
  184. crackerjack/services/version_analyzer.py +459 -0
  185. crackerjack/services/version_checker.py +1 -1
  186. crackerjack/services/websocket_resource_limiter.py +10 -144
  187. crackerjack/services/zuban_lsp_service.py +390 -0
  188. crackerjack/slash_commands/__init__.py +2 -7
  189. crackerjack/slash_commands/run.md +2 -2
  190. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  191. crackerjack/tools/validate_regex_patterns.py +19 -48
  192. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +197 -26
  193. crackerjack-0.33.1.dist-info/RECORD +229 -0
  194. crackerjack/CLAUDE.md +0 -207
  195. crackerjack/RULES.md +0 -380
  196. crackerjack/py313.py +0 -234
  197. crackerjack-0.32.0.dist-info/RECORD +0 -180
  198. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
  199. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
  200. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
@@ -8,29 +8,20 @@ import tomli_w
8
8
  import yaml
9
9
  from rich.console import Console
10
10
 
11
- from crackerjack.models.protocols import ConfigMergeServiceProtocol
12
- from crackerjack.services.filesystem import FileSystemService
13
- from crackerjack.services.git import GitService
11
+ from crackerjack.models.protocols import (
12
+ ConfigMergeServiceProtocol,
13
+ FileSystemInterface,
14
+ GitInterface,
15
+ )
14
16
  from crackerjack.services.logging import get_logger
15
17
 
16
18
 
17
19
  class ConfigMergeService(ConfigMergeServiceProtocol):
18
- """Smart configuration file merging service.
19
-
20
- Extracts and centralizes smart merge logic for:
21
- - pyproject.toml files (preserves project identity, merges tool configs)
22
- - .pre-commit-config.yaml files (adds missing repos, preserves existing hooks)
23
- - .gitignore files (merges patterns while avoiding duplicates)
24
- - Generic file appending with markers
25
-
26
- Follows crackerjack's DRY, YAGNI, KISS principles.
27
- """
28
-
29
20
  def __init__(
30
21
  self,
31
22
  console: Console,
32
- filesystem: FileSystemService,
33
- git_service: GitService,
23
+ filesystem: FileSystemInterface,
24
+ git_service: GitInterface,
34
25
  ) -> None:
35
26
  self.console = console
36
27
  self.filesystem = filesystem
@@ -43,25 +34,23 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
43
34
  target_path: str | t.Any,
44
35
  project_name: str,
45
36
  ) -> dict[str, t.Any]:
46
- """Smart merge pyproject.toml preserving project identity and merging tool configs."""
47
37
  target_path = Path(target_path)
48
38
 
49
39
  if not target_path.exists():
50
- # No existing file, return source with project name replacement
51
- return self._replace_project_name_in_config_value(
52
- source_content, project_name
40
+ return t.cast(
41
+ dict[str, t.Any],
42
+ self._replace_project_name_in_config_value(
43
+ source_content, project_name
44
+ ),
53
45
  )
54
46
 
55
47
  with target_path.open("rb") as f:
56
48
  target_content = tomli.load(f)
57
49
 
58
- # Ensure crackerjack dev dependency
59
50
  self._ensure_crackerjack_dev_dependency(target_content, source_content)
60
51
 
61
- # Merge tool configurations
62
52
  self._merge_tool_configurations(target_content, source_content, project_name)
63
53
 
64
- # Remove fixed coverage requirements (use ratchet system)
65
54
  self._remove_fixed_coverage_requirements(target_content)
66
55
 
67
56
  self.logger.info("Smart merged pyproject.toml", project_name=project_name)
@@ -73,17 +62,17 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
73
62
  target_path: str | t.Any,
74
63
  project_name: str,
75
64
  ) -> dict[str, t.Any]:
76
- """Smart merge .pre-commit-config.yaml adding missing repos."""
77
65
  target_path = Path(target_path)
78
66
 
79
67
  if not target_path.exists():
80
- # No existing file, return source
81
68
  return source_content
82
69
 
83
70
  with target_path.open() as f:
84
- target_content = yaml.safe_load(f) or {}
71
+ loaded_config = yaml.safe_load(f)
72
+ target_content: dict[str, t.Any] = (
73
+ loaded_config if isinstance(loaded_config, dict) else {}
74
+ )
85
75
 
86
- # Ensure target_content is a dict
87
76
  if not isinstance(target_content, dict):
88
77
  self.logger.warning(
89
78
  f"Target config is not a dictionary, using source: {type(target_content)}"
@@ -93,16 +82,13 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
93
82
  source_repos = source_content.get("repos", [])
94
83
  target_repos = target_content.get("repos", [])
95
84
 
96
- # Ensure target_repos is a list of dicts
97
85
  if not isinstance(target_repos, list):
98
86
  target_repos = []
99
87
 
100
- # Get existing repo URLs to avoid duplicates
101
88
  existing_repo_urls = {
102
89
  repo.get("repo", "") for repo in target_repos if isinstance(repo, dict)
103
90
  }
104
91
 
105
- # Find new repos to add
106
92
  new_repos = [
107
93
  repo
108
94
  for repo in source_repos
@@ -128,19 +114,15 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
128
114
  end_marker: str,
129
115
  force: bool = False,
130
116
  ) -> str:
131
- """Smart append content to file with markers (for CLAUDE.md, etc)."""
132
117
  target_path = Path(target_path)
133
118
 
134
119
  if not target_path.exists():
135
- # No existing file, return source content wrapped in markers
136
120
  return f"{start_marker}\n{source_content.strip()}\n{end_marker}\n"
137
121
 
138
122
  existing_content = target_path.read_text()
139
123
 
140
- # Check if markers already exist
141
124
  if start_marker in existing_content:
142
125
  if force:
143
- # Replace existing section
144
126
  start_idx = existing_content.find(start_marker)
145
127
  end_idx = existing_content.find(end_marker)
146
128
  if end_idx != -1:
@@ -149,10 +131,8 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
149
131
  existing_content[:start_idx] + existing_content[end_idx:]
150
132
  ).strip()
151
133
  else:
152
- # Already exists and not forced, return existing
153
134
  return existing_content
154
135
 
155
- # Append new section with markers
156
136
  merged_content = existing_content.strip() + "\n\n" + start_marker + "\n"
157
137
  merged_content += source_content.strip() + "\n"
158
138
  merged_content += end_marker + "\n"
@@ -165,7 +145,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
165
145
  patterns: list[str],
166
146
  target_path: str | t.Any,
167
147
  ) -> str:
168
- """Smart merge .gitignore patterns avoiding and cleaning out duplicates."""
169
148
  target_path = Path(target_path)
170
149
 
171
150
  if not target_path.exists():
@@ -173,13 +152,10 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
173
152
 
174
153
  lines = target_path.read_text().splitlines()
175
154
 
176
- # Parse existing content and extract patterns
177
155
  parsed_content = self._parse_existing_gitignore_content(lines)
178
156
 
179
- # Build merged content
180
157
  merged_content = self._build_merged_gitignore_content(parsed_content, patterns)
181
158
 
182
- # Write and log results
183
159
  target_path.write_text(merged_content)
184
160
  new_patterns_count = len(
185
161
  [p for p in patterns if p not in parsed_content.existing_patterns]
@@ -194,7 +170,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
194
170
  return merged_content
195
171
 
196
172
  def _create_new_gitignore(self, target_path: Path, patterns: list[str]) -> str:
197
- """Create a new .gitignore file with patterns."""
198
173
  merged_content = "# Crackerjack patterns\n"
199
174
  for pattern in patterns:
200
175
  merged_content += f"{pattern}\n"
@@ -203,13 +178,10 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
203
178
  return merged_content
204
179
 
205
180
  def _parse_existing_gitignore_content(self, lines: list[str]) -> t.Any:
206
- """Parse existing .gitignore content, extracting patterns and non-Crackerjack lines."""
207
-
208
- # Using a simple namespace class to group related data
209
181
  class ParsedContent:
210
- def __init__(self):
211
- self.cleaned_lines = []
212
- self.existing_patterns = set()
182
+ def __init__(self) -> None:
183
+ self.cleaned_lines: list[str] = []
184
+ self.existing_patterns: set[str] = set()
213
185
 
214
186
  parsed = ParsedContent()
215
187
  parser_state = self._init_parser_state()
@@ -220,7 +192,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
220
192
  return parsed
221
193
 
222
194
  def _init_parser_state(self) -> dict[str, bool]:
223
- """Initialize parser state for gitignore parsing."""
224
195
  return {
225
196
  "inside_crackerjack_section": False,
226
197
  "skip_empty_after_crackerjack": False,
@@ -229,60 +200,49 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
229
200
  def _process_gitignore_line(
230
201
  self, line: str, parsed: t.Any, state: dict[str, bool]
231
202
  ) -> dict[str, bool]:
232
- """Process a single line during gitignore parsing."""
233
203
  stripped = line.strip()
234
204
 
235
- # Handle Crackerjack section headers
236
205
  if self._is_crackerjack_header(stripped):
237
206
  return self._handle_crackerjack_header(state)
238
207
 
239
- # Handle empty lines after headers
240
208
  if self._should_skip_empty_line(stripped, state):
241
209
  state["skip_empty_after_crackerjack"] = False
242
210
  return state
243
211
 
244
212
  state["skip_empty_after_crackerjack"] = False
245
213
 
246
- # Process patterns and lines
247
214
  self._collect_pattern_if_present(stripped, parsed, state)
248
215
  self._add_line_if_non_crackerjack(line, parsed, state)
249
216
 
250
217
  return state
251
218
 
252
219
  def _handle_crackerjack_header(self, state: dict[str, bool]) -> dict[str, bool]:
253
- """Handle Crackerjack section header detection."""
254
220
  if not state["inside_crackerjack_section"]:
255
221
  state["inside_crackerjack_section"] = True
256
222
  state["skip_empty_after_crackerjack"] = True
257
223
  return state
258
224
 
259
225
  def _should_skip_empty_line(self, stripped: str, state: dict[str, bool]) -> bool:
260
- """Check if empty line should be skipped after Crackerjack header."""
261
226
  return state["skip_empty_after_crackerjack"] and not stripped
262
227
 
263
228
  def _collect_pattern_if_present(
264
229
  self, stripped: str, parsed: t.Any, state: dict[str, bool]
265
230
  ) -> None:
266
- """Collect gitignore pattern if present on this line."""
267
231
  if stripped and not stripped.startswith("#"):
268
232
  parsed.existing_patterns.add(stripped)
269
233
 
270
234
  def _add_line_if_non_crackerjack(
271
235
  self, line: str, parsed: t.Any, state: dict[str, bool]
272
236
  ) -> None:
273
- """Add line to cleaned output if not in Crackerjack section."""
274
237
  if not state["inside_crackerjack_section"]:
275
238
  parsed.cleaned_lines.append(line)
276
239
 
277
240
  def _is_crackerjack_header(self, line: str) -> bool:
278
- """Check if a line is a Crackerjack section header."""
279
241
  return line in ("# Crackerjack patterns", "# Crackerjack generated files")
280
242
 
281
243
  def _build_merged_gitignore_content(
282
244
  self, parsed_content: t.Any, new_patterns: list[str]
283
245
  ) -> str:
284
- """Build the final merged .gitignore content."""
285
- # Remove trailing empty line if exists
286
246
  if parsed_content.cleaned_lines and not parsed_content.cleaned_lines[-1]:
287
247
  parsed_content.cleaned_lines.pop()
288
248
 
@@ -290,7 +250,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
290
250
  if merged_content:
291
251
  merged_content += "\n"
292
252
 
293
- # Add consolidated Crackerjack section
294
253
  all_crackerjack_patterns = self._get_consolidated_patterns(
295
254
  parsed_content.existing_patterns, new_patterns
296
255
  )
@@ -305,24 +264,22 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
305
264
  def _get_consolidated_patterns(
306
265
  self, existing_patterns: set[str], new_patterns: list[str]
307
266
  ) -> list[str]:
308
- """Get consolidated list of all Crackerjack patterns."""
309
267
  new_patterns_to_add = [p for p in new_patterns if p not in existing_patterns]
310
- return list(existing_patterns) + new_patterns_to_add
268
+ return list[t.Any](existing_patterns) + new_patterns_to_add
311
269
 
312
270
  def write_pyproject_config(
313
271
  self,
314
272
  config: dict[str, t.Any],
315
273
  target_path: str | t.Any,
316
274
  ) -> None:
317
- """Write pyproject.toml config with proper formatting."""
318
275
  target_path = Path(target_path)
319
276
 
320
- # Use BytesIO for proper TOML encoding
321
277
  buffer = io.BytesIO()
322
278
  tomli_w.dump(config, buffer)
323
279
  content = buffer.getvalue().decode("utf-8")
324
280
 
325
- # Clean trailing whitespace
281
+ from crackerjack.services.filesystem import FileSystemService
282
+
326
283
  content = FileSystemService.clean_trailing_whitespace_and_newlines(content)
327
284
 
328
285
  with target_path.open("w", encoding="utf-8") as f:
@@ -335,7 +292,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
335
292
  config: dict[str, t.Any],
336
293
  target_path: str | t.Any,
337
294
  ) -> None:
338
- """Write .pre-commit-config.yaml with proper formatting."""
339
295
  target_path = Path(target_path)
340
296
 
341
297
  yaml_content = yaml.dump(
@@ -344,12 +300,10 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
344
300
  sort_keys=False,
345
301
  width=float("inf"),
346
302
  )
347
- content = (
348
- yaml_content.decode() if isinstance(yaml_content, bytes) else yaml_content
349
- )
350
- content = content or ""
303
+ content = yaml_content or ""
304
+
305
+ from crackerjack.services.filesystem import FileSystemService
351
306
 
352
- # Clean trailing whitespace
353
307
  content = FileSystemService.clean_trailing_whitespace_and_newlines(content)
354
308
 
355
309
  with target_path.open("w") as f:
@@ -362,7 +316,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
362
316
  target_config: dict[str, t.Any],
363
317
  source_config: dict[str, t.Any],
364
318
  ) -> None:
365
- """Ensure crackerjack is in dev dependencies."""
366
319
  if "dependency-groups" not in target_config:
367
320
  target_config["dependency-groups"] = {}
368
321
 
@@ -380,7 +333,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
380
333
  source_config: dict[str, t.Any],
381
334
  project_name: str,
382
335
  ) -> None:
383
- """Merge tool configurations from source to target."""
384
336
  source_tools = source_config.get("tool", {})
385
337
 
386
338
  if "tool" not in target_config:
@@ -416,7 +368,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
416
368
  project_name,
417
369
  )
418
370
 
419
- # Merge pytest markers
420
371
  self._merge_pytest_markers(target_tools, source_tools)
421
372
 
422
373
  def _merge_tool_settings(
@@ -426,7 +377,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
426
377
  tool_name: str,
427
378
  project_name: str,
428
379
  ) -> None:
429
- """Merge individual tool settings."""
430
380
  updated_keys = []
431
381
 
432
382
  for key, value in source_tool.items():
@@ -446,7 +396,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
446
396
  target_tools: dict[str, t.Any],
447
397
  source_tools: dict[str, t.Any],
448
398
  ) -> None:
449
- """Merge pytest markers avoiding duplicates."""
450
399
  if "pytest" not in source_tools or "pytest" not in target_tools:
451
400
  return
452
401
 
@@ -459,7 +408,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
459
408
  source_markers = source_pytest["ini_options"].get("markers", [])
460
409
  target_markers = target_pytest["ini_options"].get("markers", [])
461
410
 
462
- # Extract existing marker names
463
411
  existing_marker_names = {marker.split(": ")[0] for marker in target_markers}
464
412
  new_markers = [
465
413
  marker
@@ -477,21 +425,18 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
477
425
  self,
478
426
  target_config: dict[str, t.Any],
479
427
  ) -> None:
480
- """Remove fixed coverage requirements to use ratchet system."""
481
428
  target_coverage = (
482
429
  target_config.get("tool", {}).get("pytest", {}).get("ini_options", {})
483
430
  )
484
431
 
485
- # Remove --cov-fail-under from addopts
486
432
  addopts = target_coverage.get("addopts", "")
487
433
  if isinstance(addopts, str):
488
434
  original_addopts = addopts
489
435
 
490
- # Remove coverage fail-under flags
491
436
  from crackerjack.services.regex_patterns import remove_coverage_fail_under
492
437
 
493
438
  addopts = remove_coverage_fail_under(addopts).strip()
494
- addopts = " ".join(addopts.split()) # Normalize whitespace
439
+ addopts = " ".join(addopts.split())
495
440
 
496
441
  if original_addopts != addopts:
497
442
  target_coverage["addopts"] = addopts
@@ -499,7 +444,6 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
499
444
  "[green]🔄[/green] Removed fixed coverage requirement (using ratchet system)"
500
445
  )
501
446
 
502
- # Reset coverage.report.fail_under to 0
503
447
  coverage_report = (
504
448
  target_config.get("tool", {}).get("coverage", {}).get("report", {})
505
449
  )
@@ -513,17 +457,18 @@ class ConfigMergeService(ConfigMergeServiceProtocol):
513
457
  def _replace_project_name_in_tool_config(
514
458
  self, tool_config: dict[str, t.Any], project_name: str
515
459
  ) -> dict[str, t.Any]:
516
- """Replace project name in tool configuration."""
517
460
  if project_name == "crackerjack":
518
461
  return tool_config
519
462
 
520
463
  result = copy.deepcopy(tool_config)
521
- return self._replace_project_name_in_config_value(result, project_name)
464
+ return t.cast(
465
+ dict[str, t.Any],
466
+ self._replace_project_name_in_config_value(result, project_name),
467
+ )
522
468
 
523
469
  def _replace_project_name_in_config_value(
524
470
  self, value: t.Any, project_name: str
525
471
  ) -> t.Any:
526
- """Recursively replace project name in configuration values."""
527
472
  if project_name == "crackerjack":
528
473
  return value
529
474