crackerjack 0.29.0__py3-none-any.whl → 0.31.4__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 (158) hide show
  1. crackerjack/CLAUDE.md +1005 -0
  2. crackerjack/RULES.md +380 -0
  3. crackerjack/__init__.py +42 -13
  4. crackerjack/__main__.py +225 -253
  5. crackerjack/agents/__init__.py +41 -0
  6. crackerjack/agents/architect_agent.py +281 -0
  7. crackerjack/agents/base.py +169 -0
  8. crackerjack/agents/coordinator.py +512 -0
  9. crackerjack/agents/documentation_agent.py +498 -0
  10. crackerjack/agents/dry_agent.py +388 -0
  11. crackerjack/agents/formatting_agent.py +245 -0
  12. crackerjack/agents/import_optimization_agent.py +281 -0
  13. crackerjack/agents/performance_agent.py +669 -0
  14. crackerjack/agents/proactive_agent.py +104 -0
  15. crackerjack/agents/refactoring_agent.py +788 -0
  16. crackerjack/agents/security_agent.py +529 -0
  17. crackerjack/agents/test_creation_agent.py +652 -0
  18. crackerjack/agents/test_specialist_agent.py +486 -0
  19. crackerjack/agents/tracker.py +212 -0
  20. crackerjack/api.py +560 -0
  21. crackerjack/cli/__init__.py +24 -0
  22. crackerjack/cli/facade.py +104 -0
  23. crackerjack/cli/handlers.py +267 -0
  24. crackerjack/cli/interactive.py +471 -0
  25. crackerjack/cli/options.py +401 -0
  26. crackerjack/cli/utils.py +18 -0
  27. crackerjack/code_cleaner.py +670 -0
  28. crackerjack/config/__init__.py +19 -0
  29. crackerjack/config/hooks.py +218 -0
  30. crackerjack/core/__init__.py +0 -0
  31. crackerjack/core/async_workflow_orchestrator.py +406 -0
  32. crackerjack/core/autofix_coordinator.py +200 -0
  33. crackerjack/core/container.py +104 -0
  34. crackerjack/core/enhanced_container.py +542 -0
  35. crackerjack/core/performance.py +243 -0
  36. crackerjack/core/phase_coordinator.py +561 -0
  37. crackerjack/core/proactive_workflow.py +316 -0
  38. crackerjack/core/session_coordinator.py +289 -0
  39. crackerjack/core/workflow_orchestrator.py +640 -0
  40. crackerjack/dynamic_config.py +577 -0
  41. crackerjack/errors.py +263 -41
  42. crackerjack/executors/__init__.py +11 -0
  43. crackerjack/executors/async_hook_executor.py +431 -0
  44. crackerjack/executors/cached_hook_executor.py +242 -0
  45. crackerjack/executors/hook_executor.py +345 -0
  46. crackerjack/executors/individual_hook_executor.py +669 -0
  47. crackerjack/intelligence/__init__.py +44 -0
  48. crackerjack/intelligence/adaptive_learning.py +751 -0
  49. crackerjack/intelligence/agent_orchestrator.py +551 -0
  50. crackerjack/intelligence/agent_registry.py +414 -0
  51. crackerjack/intelligence/agent_selector.py +502 -0
  52. crackerjack/intelligence/integration.py +290 -0
  53. crackerjack/interactive.py +576 -315
  54. crackerjack/managers/__init__.py +11 -0
  55. crackerjack/managers/async_hook_manager.py +135 -0
  56. crackerjack/managers/hook_manager.py +137 -0
  57. crackerjack/managers/publish_manager.py +411 -0
  58. crackerjack/managers/test_command_builder.py +151 -0
  59. crackerjack/managers/test_executor.py +435 -0
  60. crackerjack/managers/test_manager.py +258 -0
  61. crackerjack/managers/test_manager_backup.py +1124 -0
  62. crackerjack/managers/test_progress.py +144 -0
  63. crackerjack/mcp/__init__.py +0 -0
  64. crackerjack/mcp/cache.py +336 -0
  65. crackerjack/mcp/client_runner.py +104 -0
  66. crackerjack/mcp/context.py +615 -0
  67. crackerjack/mcp/dashboard.py +636 -0
  68. crackerjack/mcp/enhanced_progress_monitor.py +479 -0
  69. crackerjack/mcp/file_monitor.py +336 -0
  70. crackerjack/mcp/progress_components.py +569 -0
  71. crackerjack/mcp/progress_monitor.py +949 -0
  72. crackerjack/mcp/rate_limiter.py +332 -0
  73. crackerjack/mcp/server.py +22 -0
  74. crackerjack/mcp/server_core.py +244 -0
  75. crackerjack/mcp/service_watchdog.py +501 -0
  76. crackerjack/mcp/state.py +395 -0
  77. crackerjack/mcp/task_manager.py +257 -0
  78. crackerjack/mcp/tools/__init__.py +17 -0
  79. crackerjack/mcp/tools/core_tools.py +249 -0
  80. crackerjack/mcp/tools/error_analyzer.py +308 -0
  81. crackerjack/mcp/tools/execution_tools.py +370 -0
  82. crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
  83. crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
  84. crackerjack/mcp/tools/intelligence_tools.py +314 -0
  85. crackerjack/mcp/tools/monitoring_tools.py +502 -0
  86. crackerjack/mcp/tools/proactive_tools.py +384 -0
  87. crackerjack/mcp/tools/progress_tools.py +141 -0
  88. crackerjack/mcp/tools/utility_tools.py +341 -0
  89. crackerjack/mcp/tools/workflow_executor.py +360 -0
  90. crackerjack/mcp/websocket/__init__.py +14 -0
  91. crackerjack/mcp/websocket/app.py +39 -0
  92. crackerjack/mcp/websocket/endpoints.py +559 -0
  93. crackerjack/mcp/websocket/jobs.py +253 -0
  94. crackerjack/mcp/websocket/server.py +116 -0
  95. crackerjack/mcp/websocket/websocket_handler.py +78 -0
  96. crackerjack/mcp/websocket_server.py +10 -0
  97. crackerjack/models/__init__.py +31 -0
  98. crackerjack/models/config.py +93 -0
  99. crackerjack/models/config_adapter.py +230 -0
  100. crackerjack/models/protocols.py +118 -0
  101. crackerjack/models/task.py +154 -0
  102. crackerjack/monitoring/ai_agent_watchdog.py +450 -0
  103. crackerjack/monitoring/regression_prevention.py +638 -0
  104. crackerjack/orchestration/__init__.py +0 -0
  105. crackerjack/orchestration/advanced_orchestrator.py +970 -0
  106. crackerjack/orchestration/execution_strategies.py +341 -0
  107. crackerjack/orchestration/test_progress_streamer.py +636 -0
  108. crackerjack/plugins/__init__.py +15 -0
  109. crackerjack/plugins/base.py +200 -0
  110. crackerjack/plugins/hooks.py +246 -0
  111. crackerjack/plugins/loader.py +335 -0
  112. crackerjack/plugins/managers.py +259 -0
  113. crackerjack/py313.py +8 -3
  114. crackerjack/services/__init__.py +22 -0
  115. crackerjack/services/cache.py +314 -0
  116. crackerjack/services/config.py +347 -0
  117. crackerjack/services/config_integrity.py +99 -0
  118. crackerjack/services/contextual_ai_assistant.py +516 -0
  119. crackerjack/services/coverage_ratchet.py +347 -0
  120. crackerjack/services/debug.py +736 -0
  121. crackerjack/services/dependency_monitor.py +617 -0
  122. crackerjack/services/enhanced_filesystem.py +439 -0
  123. crackerjack/services/file_hasher.py +151 -0
  124. crackerjack/services/filesystem.py +395 -0
  125. crackerjack/services/git.py +165 -0
  126. crackerjack/services/health_metrics.py +611 -0
  127. crackerjack/services/initialization.py +847 -0
  128. crackerjack/services/log_manager.py +286 -0
  129. crackerjack/services/logging.py +174 -0
  130. crackerjack/services/metrics.py +578 -0
  131. crackerjack/services/pattern_cache.py +362 -0
  132. crackerjack/services/pattern_detector.py +515 -0
  133. crackerjack/services/performance_benchmarks.py +653 -0
  134. crackerjack/services/security.py +163 -0
  135. crackerjack/services/server_manager.py +234 -0
  136. crackerjack/services/smart_scheduling.py +144 -0
  137. crackerjack/services/tool_version_service.py +61 -0
  138. crackerjack/services/unified_config.py +437 -0
  139. crackerjack/services/version_checker.py +248 -0
  140. crackerjack/slash_commands/__init__.py +14 -0
  141. crackerjack/slash_commands/init.md +122 -0
  142. crackerjack/slash_commands/run.md +163 -0
  143. crackerjack/slash_commands/status.md +127 -0
  144. crackerjack-0.31.4.dist-info/METADATA +742 -0
  145. crackerjack-0.31.4.dist-info/RECORD +148 -0
  146. crackerjack-0.31.4.dist-info/entry_points.txt +2 -0
  147. crackerjack/.gitignore +0 -34
  148. crackerjack/.libcst.codemod.yaml +0 -18
  149. crackerjack/.pdm.toml +0 -1
  150. crackerjack/.pre-commit-config-ai.yaml +0 -149
  151. crackerjack/.pre-commit-config-fast.yaml +0 -69
  152. crackerjack/.pre-commit-config.yaml +0 -114
  153. crackerjack/crackerjack.py +0 -4140
  154. crackerjack/pyproject.toml +0 -285
  155. crackerjack-0.29.0.dist-info/METADATA +0 -1289
  156. crackerjack-0.29.0.dist-info/RECORD +0 -17
  157. {crackerjack-0.29.0.dist-info → crackerjack-0.31.4.dist-info}/WHEEL +0 -0
  158. {crackerjack-0.29.0.dist-info → crackerjack-0.31.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,542 @@
1
+ """Enhanced dependency injection container with lifecycle management."""
2
+
3
+ import inspect
4
+ import threading
5
+ import typing as t
6
+ from collections.abc import Callable
7
+ from dataclasses import dataclass, field
8
+ from enum import Enum
9
+ from pathlib import Path
10
+ from typing import Any, TypeVar
11
+
12
+ from rich.console import Console
13
+
14
+ from crackerjack.models.protocols import (
15
+ FileSystemInterface,
16
+ GitInterface,
17
+ HookManager,
18
+ PublishManager,
19
+ TestManagerProtocol,
20
+ )
21
+ from crackerjack.services.logging import get_logger
22
+
23
+ T = TypeVar("T")
24
+ FactoryFunc = Callable[..., T]
25
+
26
+
27
+ class ServiceLifetime(Enum):
28
+ """Service lifetime enumeration."""
29
+
30
+ SINGLETON = "singleton"
31
+ TRANSIENT = "transient"
32
+ SCOPED = "scoped"
33
+
34
+
35
+ @dataclass
36
+ class ServiceDescriptor:
37
+ """Describes how to create a service instance."""
38
+
39
+ interface: type
40
+ implementation: type | None = None
41
+ factory: Callable[..., Any] | None = None
42
+ lifetime: ServiceLifetime = ServiceLifetime.TRANSIENT
43
+ instance: Any | None = None
44
+ created_count: int = 0
45
+ dependencies: list[type] = field(default_factory=list)
46
+
47
+ def __post_init__(self):
48
+ if self.implementation is self.factory is self.instance is None:
49
+ msg = "Must provide either implementation, factory, or instance"
50
+ raise ValueError(msg)
51
+
52
+
53
+ class ServiceScope:
54
+ """Represents a service scope for scoped services."""
55
+
56
+ def __init__(self, name: str) -> None:
57
+ self.name = name
58
+ self._instances: dict[str, Any] = {}
59
+ self._lock = threading.Lock()
60
+ self.logger = get_logger("crackerjack.container.scope")
61
+
62
+ def get_instance(self, key: str) -> Any | None:
63
+ """Get scoped instance."""
64
+ with self._lock:
65
+ return self._instances.get(key)
66
+
67
+ def set_instance(self, key: str, instance: Any) -> None:
68
+ """Set scoped instance."""
69
+ with self._lock:
70
+ self._instances[key] = instance
71
+ self.logger.debug("Scoped instance created", scope=self.name, service=key)
72
+
73
+ def dispose(self) -> None:
74
+ """Dispose of all scoped instances."""
75
+ with self._lock:
76
+ for key, instance in self._instances.items():
77
+ if hasattr(instance, "dispose"):
78
+ try:
79
+ instance.dispose()
80
+ except Exception as e:
81
+ self.logger.exception(
82
+ "Error disposing service",
83
+ service=key,
84
+ error=str(e),
85
+ )
86
+
87
+ self._instances.clear()
88
+ self.logger.info("Service scope disposed", scope=self.name)
89
+
90
+
91
+ class DependencyResolver:
92
+ """Resolves service dependencies through constructor injection."""
93
+
94
+ def __init__(self, container: "EnhancedDependencyContainer") -> None:
95
+ self.container = container
96
+ self.logger = get_logger("crackerjack.container.resolver")
97
+
98
+ def create_instance(
99
+ self,
100
+ descriptor: ServiceDescriptor,
101
+ scope: ServiceScope | None = None,
102
+ ) -> Any:
103
+ """Create service instance with dependency injection."""
104
+ if descriptor.instance is not None:
105
+ return descriptor.instance
106
+
107
+ if descriptor.factory is not None:
108
+ return self._create_from_factory(descriptor.factory)
109
+
110
+ if descriptor.implementation is not None:
111
+ return self._create_from_class(descriptor.implementation)
112
+
113
+ msg = f"Cannot create instance for {descriptor.interface}"
114
+ raise ValueError(msg)
115
+
116
+ def _create_from_factory(self, factory: Callable[..., Any]) -> Any:
117
+ """Create instance using factory function."""
118
+ sig = inspect.signature(factory)
119
+ kwargs = {}
120
+
121
+ for param_name, param in sig.parameters.items():
122
+ if param.annotation != inspect.Parameter.empty:
123
+ try:
124
+ dependency = self.container.get(param.annotation)
125
+ kwargs[param_name] = dependency
126
+ except Exception as e:
127
+ self.logger.warning(
128
+ "Could not inject dependency",
129
+ parameter=param_name,
130
+ type=param.annotation,
131
+ error=str(e),
132
+ )
133
+
134
+ return factory(**kwargs)
135
+
136
+ def _create_from_class(self, implementation: type) -> Any:
137
+ """Create instance using class constructor with dependency injection."""
138
+ try:
139
+ kwargs = self._build_constructor_kwargs(implementation)
140
+ return self._instantiate_with_logging(implementation, kwargs)
141
+ except Exception as e:
142
+ self.logger.exception(
143
+ "Failed to create instance",
144
+ implementation_class=implementation.__name__,
145
+ error=str(e),
146
+ )
147
+ raise
148
+
149
+ def _build_constructor_kwargs(self, implementation: type) -> dict[str, Any]:
150
+ """Build constructor kwargs by resolving dependencies."""
151
+ init_sig = inspect.signature(implementation.__init__)
152
+ kwargs = {}
153
+
154
+ for param_name, param in init_sig.parameters.items():
155
+ if param_name == "self":
156
+ continue
157
+
158
+ if param.annotation != inspect.Parameter.empty:
159
+ self._resolve_parameter_dependency(
160
+ kwargs, param_name, param, implementation.__name__
161
+ )
162
+
163
+ return kwargs
164
+
165
+ def _resolve_parameter_dependency(
166
+ self,
167
+ kwargs: dict[str, Any],
168
+ param_name: str,
169
+ param: inspect.Parameter,
170
+ class_name: str,
171
+ ) -> None:
172
+ """Resolve a single parameter dependency."""
173
+ try:
174
+ dependency = self.container.get(param.annotation)
175
+ kwargs[param_name] = dependency
176
+ except Exception as e:
177
+ if param.default == inspect.Parameter.empty:
178
+ self.logger.exception(
179
+ "Required dependency not available",
180
+ implementation_class=class_name,
181
+ parameter=param_name,
182
+ type=param.annotation,
183
+ error=str(e),
184
+ )
185
+ raise
186
+ self.logger.debug(
187
+ "Optional dependency not available, using default",
188
+ parameter=param_name,
189
+ type=param.annotation,
190
+ )
191
+
192
+ def _instantiate_with_logging(
193
+ self, implementation: type, kwargs: dict[str, Any]
194
+ ) -> Any:
195
+ """Create instance and log the creation."""
196
+ instance = implementation(**kwargs)
197
+ self.logger.debug(
198
+ "Instance created with DI",
199
+ implementation_class=implementation.__name__,
200
+ )
201
+ return instance
202
+
203
+
204
+ class EnhancedDependencyContainer:
205
+ """Enhanced dependency injection container with lifecycle management."""
206
+
207
+ def __init__(self, name: str = "default") -> None:
208
+ self.name = name
209
+ self._services: dict[str, ServiceDescriptor] = {}
210
+ self._singletons: dict[str, Any] = {}
211
+ self._lock = threading.Lock()
212
+ self._current_scope: ServiceScope | None = None
213
+ self.resolver = DependencyResolver(self)
214
+ self.logger = get_logger("crackerjack.container")
215
+
216
+ def register_singleton(
217
+ self,
218
+ interface: type,
219
+ implementation: type | None = None,
220
+ factory: Callable[..., Any] | None = None,
221
+ instance: Any | None = None,
222
+ ) -> "EnhancedDependencyContainer":
223
+ """Register a singleton service."""
224
+ key = self._get_service_key(interface)
225
+
226
+ descriptor = ServiceDescriptor(
227
+ interface=interface,
228
+ implementation=implementation,
229
+ factory=factory,
230
+ instance=instance,
231
+ lifetime=ServiceLifetime.SINGLETON,
232
+ )
233
+
234
+ with self._lock:
235
+ self._services[key] = descriptor
236
+
237
+ self.logger.debug("Singleton registered", interface=interface.__name__)
238
+ return self
239
+
240
+ def register_transient(
241
+ self,
242
+ interface: type,
243
+ implementation: type | None = None,
244
+ factory: Callable[..., Any] | None = None,
245
+ ) -> "EnhancedDependencyContainer":
246
+ """Register a transient service."""
247
+ key = self._get_service_key(interface)
248
+
249
+ descriptor = ServiceDescriptor(
250
+ interface=interface,
251
+ implementation=implementation,
252
+ factory=factory,
253
+ lifetime=ServiceLifetime.TRANSIENT,
254
+ )
255
+
256
+ with self._lock:
257
+ self._services[key] = descriptor
258
+
259
+ self.logger.debug("Transient registered", interface=interface.__name__)
260
+ return self
261
+
262
+ def register_scoped(
263
+ self,
264
+ interface: type,
265
+ implementation: type | None = None,
266
+ factory: Callable[..., Any] | None = None,
267
+ ) -> "EnhancedDependencyContainer":
268
+ """Register a scoped service."""
269
+ key = self._get_service_key(interface)
270
+
271
+ descriptor = ServiceDescriptor(
272
+ interface=interface,
273
+ implementation=implementation,
274
+ factory=factory,
275
+ lifetime=ServiceLifetime.SCOPED,
276
+ )
277
+
278
+ with self._lock:
279
+ self._services[key] = descriptor
280
+
281
+ self.logger.debug("Scoped registered", interface=interface.__name__)
282
+ return self
283
+
284
+ def get(self, interface: type, scope: ServiceScope | None = None) -> Any:
285
+ """Get service instance."""
286
+ key = self._get_service_key(interface)
287
+
288
+ with self._lock:
289
+ if key not in self._services:
290
+ msg = f"Service {interface.__name__} not registered"
291
+ raise ValueError(msg)
292
+
293
+ descriptor = self._services[key]
294
+
295
+ return self._create_service_instance(descriptor, scope or self._current_scope)
296
+
297
+ def get_optional(self, interface: type, default: Any = None) -> Any:
298
+ """Get service instance or return default if not registered."""
299
+ try:
300
+ return self.get(interface)
301
+ except ValueError:
302
+ return default
303
+
304
+ def is_registered(self, interface: type) -> bool:
305
+ """Check if service is registered."""
306
+ key = self._get_service_key(interface)
307
+ return key in self._services
308
+
309
+ def create_scope(self, name: str = "scope") -> ServiceScope:
310
+ """Create a new service scope."""
311
+ return ServiceScope(name)
312
+
313
+ def set_current_scope(self, scope: ServiceScope | None) -> None:
314
+ """Set the current service scope."""
315
+ self._current_scope = scope
316
+
317
+ def get_service_info(self) -> dict[str, Any]:
318
+ """Get information about registered services."""
319
+ info = {}
320
+
321
+ with self._lock:
322
+ for key, descriptor in self._services.items():
323
+ info[key] = {
324
+ "interface": descriptor.interface.__name__,
325
+ "implementation": descriptor.implementation.__name__
326
+ if descriptor.implementation
327
+ else None,
328
+ "lifetime": descriptor.lifetime.value,
329
+ "created_count": descriptor.created_count,
330
+ "has_instance": descriptor.instance is not None,
331
+ }
332
+
333
+ return info
334
+
335
+ def dispose(self) -> None:
336
+ """Dispose of container and all singletons."""
337
+ with self._lock:
338
+ # Dispose singletons
339
+ for key, instance in self._singletons.items():
340
+ if hasattr(instance, "dispose"):
341
+ try:
342
+ instance.dispose()
343
+ except Exception as e:
344
+ self.logger.exception(
345
+ "Error disposing singleton",
346
+ service=key,
347
+ error=str(e),
348
+ )
349
+
350
+ self._singletons.clear()
351
+
352
+ # Dispose current scope
353
+ if self._current_scope:
354
+ self._current_scope.dispose()
355
+ self._current_scope = None
356
+
357
+ self.logger.info("Container disposed", name=self.name)
358
+
359
+ def _create_service_instance(
360
+ self,
361
+ descriptor: ServiceDescriptor,
362
+ scope: ServiceScope | None = None,
363
+ ) -> Any:
364
+ """Create service instance based on lifetime."""
365
+ if descriptor.lifetime == ServiceLifetime.SINGLETON:
366
+ return self._get_or_create_singleton(descriptor)
367
+ if descriptor.lifetime == ServiceLifetime.SCOPED:
368
+ return self._get_or_create_scoped(descriptor, scope)
369
+ # Transient
370
+ return self._create_transient_instance(descriptor)
371
+
372
+ def _get_or_create_singleton(self, descriptor: ServiceDescriptor) -> Any:
373
+ """Get or create singleton instance."""
374
+ key = self._get_service_key(descriptor.interface)
375
+
376
+ if key in self._singletons:
377
+ return self._singletons[key]
378
+
379
+ instance = self.resolver.create_instance(descriptor)
380
+ self._singletons[key] = instance
381
+ descriptor.created_count += 1
382
+
383
+ self.logger.debug("Singleton created", interface=descriptor.interface.__name__)
384
+ return instance
385
+
386
+ def _get_or_create_scoped(
387
+ self,
388
+ descriptor: ServiceDescriptor,
389
+ scope: ServiceScope | None,
390
+ ) -> Any:
391
+ """Get or create scoped instance."""
392
+ if scope is None:
393
+ msg = f"Scoped service {descriptor.interface.__name__} requires an active scope"
394
+ raise ValueError(
395
+ msg,
396
+ )
397
+
398
+ key = self._get_service_key(descriptor.interface)
399
+ instance = scope.get_instance(key)
400
+
401
+ if instance is None:
402
+ instance = self.resolver.create_instance(descriptor, scope)
403
+ scope.set_instance(key, instance)
404
+ descriptor.created_count += 1
405
+
406
+ return instance
407
+
408
+ def _create_transient_instance(self, descriptor: ServiceDescriptor) -> Any:
409
+ """Create new transient instance."""
410
+ instance = self.resolver.create_instance(descriptor)
411
+ descriptor.created_count += 1
412
+ return instance
413
+
414
+ def _get_service_key(self, interface: type) -> str:
415
+ """Get service key from interface type."""
416
+ return f"{interface.__module__}.{interface.__name__}"
417
+
418
+ def __enter__(self):
419
+ return self
420
+
421
+ def __exit__(
422
+ self,
423
+ exc_type: type[BaseException] | None,
424
+ exc_val: BaseException | None,
425
+ _exc_tb: t.Any,
426
+ ) -> None:
427
+ self.dispose()
428
+
429
+
430
+ class ServiceCollectionBuilder:
431
+ """Builder pattern for configuring services."""
432
+
433
+ def __init__(self, container: EnhancedDependencyContainer) -> None:
434
+ self.container = container
435
+ self.console: Console | None = None
436
+ self.pkg_path: Path | None = None
437
+ self.dry_run: bool = False
438
+
439
+ def with_console(self, console: Console) -> "ServiceCollectionBuilder":
440
+ """Set console for services that need it."""
441
+ self.console = console
442
+ return self
443
+
444
+ def with_package_path(self, pkg_path: Path) -> "ServiceCollectionBuilder":
445
+ """Set package path for services that need it."""
446
+ self.pkg_path = pkg_path
447
+ return self
448
+
449
+ def with_dry_run(self, dry_run: bool) -> "ServiceCollectionBuilder":
450
+ """Set dry run mode."""
451
+ self.dry_run = dry_run
452
+ return self
453
+
454
+ def add_core_services(self) -> "ServiceCollectionBuilder":
455
+ """Add core Crackerjack services."""
456
+ console = self.console or Console(force_terminal=True)
457
+ pkg_path = self.pkg_path or Path.cwd()
458
+
459
+ # Enhanced filesystem service
460
+ from crackerjack.services.enhanced_filesystem import EnhancedFileSystemService
461
+
462
+ self.container.register_singleton(
463
+ FileSystemInterface,
464
+ factory=EnhancedFileSystemService,
465
+ )
466
+
467
+ # Git service
468
+ from crackerjack.services.git import GitService
469
+
470
+ self.container.register_transient(
471
+ GitInterface,
472
+ factory=lambda: GitService(console=console, pkg_path=pkg_path),
473
+ )
474
+
475
+ # Async hook manager
476
+ from crackerjack.managers.async_hook_manager import AsyncHookManager
477
+
478
+ self.container.register_scoped(
479
+ HookManager,
480
+ factory=lambda: AsyncHookManager(console=console, pkg_path=pkg_path),
481
+ )
482
+
483
+ # Test manager
484
+ from crackerjack.managers.test_manager import TestManagementImpl
485
+
486
+ self.container.register_transient(
487
+ TestManagerProtocol,
488
+ factory=lambda: TestManagementImpl(console=console, pkg_path=pkg_path),
489
+ )
490
+
491
+ # Publish manager
492
+ from crackerjack.managers.publish_manager import PublishManagerImpl
493
+
494
+ self.container.register_transient(
495
+ PublishManager,
496
+ factory=lambda: PublishManagerImpl(
497
+ console=console,
498
+ pkg_path=pkg_path,
499
+ dry_run=self.dry_run,
500
+ ),
501
+ )
502
+
503
+ return self
504
+
505
+ def add_configuration_services(self) -> "ServiceCollectionBuilder":
506
+ """Add configuration services."""
507
+ console = self.console or Console(force_terminal=True)
508
+ pkg_path = self.pkg_path or Path.cwd()
509
+
510
+ # Unified configuration service
511
+ from crackerjack.services.unified_config import UnifiedConfigurationService
512
+
513
+ self.container.register_singleton(
514
+ UnifiedConfigurationService,
515
+ factory=lambda: UnifiedConfigurationService(console, pkg_path),
516
+ )
517
+
518
+ return self
519
+
520
+ def build(self) -> EnhancedDependencyContainer:
521
+ """Build the configured container."""
522
+ return self.container
523
+
524
+
525
+ def create_enhanced_container(
526
+ console: Console | None = None,
527
+ pkg_path: Path | None = None,
528
+ dry_run: bool = False,
529
+ name: str = "crackerjack",
530
+ ) -> EnhancedDependencyContainer:
531
+ """Create enhanced dependency injection container with default services."""
532
+ container = EnhancedDependencyContainer(name)
533
+
534
+ builder = ServiceCollectionBuilder(container)
535
+ builder.with_console(console or Console(force_terminal=True))
536
+ builder.with_package_path(pkg_path or Path.cwd())
537
+ builder.with_dry_run(dry_run)
538
+
539
+ builder.add_core_services()
540
+ builder.add_configuration_services()
541
+
542
+ return builder.build()