crackerjack 0.31.10__py3-none-any.whl → 0.31.13__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 (155) hide show
  1. crackerjack/CLAUDE.md +288 -705
  2. crackerjack/__main__.py +22 -8
  3. crackerjack/agents/__init__.py +0 -3
  4. crackerjack/agents/architect_agent.py +0 -43
  5. crackerjack/agents/base.py +1 -9
  6. crackerjack/agents/coordinator.py +2 -148
  7. crackerjack/agents/documentation_agent.py +109 -81
  8. crackerjack/agents/dry_agent.py +122 -97
  9. crackerjack/agents/formatting_agent.py +3 -16
  10. crackerjack/agents/import_optimization_agent.py +1174 -130
  11. crackerjack/agents/performance_agent.py +956 -188
  12. crackerjack/agents/performance_helpers.py +229 -0
  13. crackerjack/agents/proactive_agent.py +1 -48
  14. crackerjack/agents/refactoring_agent.py +516 -246
  15. crackerjack/agents/refactoring_helpers.py +282 -0
  16. crackerjack/agents/security_agent.py +393 -90
  17. crackerjack/agents/test_creation_agent.py +1776 -120
  18. crackerjack/agents/test_specialist_agent.py +59 -15
  19. crackerjack/agents/tracker.py +0 -102
  20. crackerjack/api.py +145 -37
  21. crackerjack/cli/handlers.py +48 -30
  22. crackerjack/cli/interactive.py +11 -11
  23. crackerjack/cli/options.py +66 -4
  24. crackerjack/code_cleaner.py +808 -148
  25. crackerjack/config/global_lock_config.py +110 -0
  26. crackerjack/config/hooks.py +43 -64
  27. crackerjack/core/async_workflow_orchestrator.py +247 -97
  28. crackerjack/core/autofix_coordinator.py +192 -109
  29. crackerjack/core/enhanced_container.py +46 -63
  30. crackerjack/core/file_lifecycle.py +549 -0
  31. crackerjack/core/performance.py +9 -8
  32. crackerjack/core/performance_monitor.py +395 -0
  33. crackerjack/core/phase_coordinator.py +281 -94
  34. crackerjack/core/proactive_workflow.py +9 -58
  35. crackerjack/core/resource_manager.py +501 -0
  36. crackerjack/core/service_watchdog.py +490 -0
  37. crackerjack/core/session_coordinator.py +4 -8
  38. crackerjack/core/timeout_manager.py +504 -0
  39. crackerjack/core/websocket_lifecycle.py +475 -0
  40. crackerjack/core/workflow_orchestrator.py +343 -209
  41. crackerjack/dynamic_config.py +50 -9
  42. crackerjack/errors.py +3 -4
  43. crackerjack/executors/async_hook_executor.py +63 -13
  44. crackerjack/executors/cached_hook_executor.py +14 -14
  45. crackerjack/executors/hook_executor.py +100 -37
  46. crackerjack/executors/hook_lock_manager.py +856 -0
  47. crackerjack/executors/individual_hook_executor.py +120 -86
  48. crackerjack/intelligence/__init__.py +0 -7
  49. crackerjack/intelligence/adaptive_learning.py +13 -86
  50. crackerjack/intelligence/agent_orchestrator.py +15 -78
  51. crackerjack/intelligence/agent_registry.py +12 -59
  52. crackerjack/intelligence/agent_selector.py +31 -92
  53. crackerjack/intelligence/integration.py +1 -41
  54. crackerjack/interactive.py +9 -9
  55. crackerjack/managers/async_hook_manager.py +25 -8
  56. crackerjack/managers/hook_manager.py +9 -9
  57. crackerjack/managers/publish_manager.py +57 -59
  58. crackerjack/managers/test_command_builder.py +6 -36
  59. crackerjack/managers/test_executor.py +9 -61
  60. crackerjack/managers/test_manager.py +17 -63
  61. crackerjack/managers/test_manager_backup.py +77 -127
  62. crackerjack/managers/test_progress.py +4 -23
  63. crackerjack/mcp/cache.py +5 -12
  64. crackerjack/mcp/client_runner.py +10 -10
  65. crackerjack/mcp/context.py +64 -6
  66. crackerjack/mcp/dashboard.py +14 -11
  67. crackerjack/mcp/enhanced_progress_monitor.py +55 -55
  68. crackerjack/mcp/file_monitor.py +72 -42
  69. crackerjack/mcp/progress_components.py +103 -84
  70. crackerjack/mcp/progress_monitor.py +122 -49
  71. crackerjack/mcp/rate_limiter.py +12 -12
  72. crackerjack/mcp/server_core.py +16 -22
  73. crackerjack/mcp/service_watchdog.py +26 -26
  74. crackerjack/mcp/state.py +15 -0
  75. crackerjack/mcp/tools/core_tools.py +95 -39
  76. crackerjack/mcp/tools/error_analyzer.py +6 -32
  77. crackerjack/mcp/tools/execution_tools.py +1 -56
  78. crackerjack/mcp/tools/execution_tools_backup.py +35 -131
  79. crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
  80. crackerjack/mcp/tools/intelligence_tools.py +2 -55
  81. crackerjack/mcp/tools/monitoring_tools.py +308 -145
  82. crackerjack/mcp/tools/proactive_tools.py +12 -42
  83. crackerjack/mcp/tools/progress_tools.py +23 -15
  84. crackerjack/mcp/tools/utility_tools.py +3 -40
  85. crackerjack/mcp/tools/workflow_executor.py +40 -60
  86. crackerjack/mcp/websocket/app.py +0 -3
  87. crackerjack/mcp/websocket/endpoints.py +206 -268
  88. crackerjack/mcp/websocket/jobs.py +213 -66
  89. crackerjack/mcp/websocket/server.py +84 -6
  90. crackerjack/mcp/websocket/websocket_handler.py +137 -29
  91. crackerjack/models/config_adapter.py +3 -16
  92. crackerjack/models/protocols.py +162 -3
  93. crackerjack/models/resource_protocols.py +454 -0
  94. crackerjack/models/task.py +3 -3
  95. crackerjack/monitoring/__init__.py +0 -0
  96. crackerjack/monitoring/ai_agent_watchdog.py +25 -71
  97. crackerjack/monitoring/regression_prevention.py +28 -87
  98. crackerjack/orchestration/advanced_orchestrator.py +44 -78
  99. crackerjack/orchestration/coverage_improvement.py +10 -60
  100. crackerjack/orchestration/execution_strategies.py +16 -16
  101. crackerjack/orchestration/test_progress_streamer.py +61 -53
  102. crackerjack/plugins/base.py +1 -1
  103. crackerjack/plugins/managers.py +22 -20
  104. crackerjack/py313.py +65 -21
  105. crackerjack/services/backup_service.py +467 -0
  106. crackerjack/services/bounded_status_operations.py +627 -0
  107. crackerjack/services/cache.py +7 -9
  108. crackerjack/services/config.py +35 -52
  109. crackerjack/services/config_integrity.py +5 -16
  110. crackerjack/services/config_merge.py +542 -0
  111. crackerjack/services/contextual_ai_assistant.py +17 -19
  112. crackerjack/services/coverage_ratchet.py +44 -73
  113. crackerjack/services/debug.py +25 -39
  114. crackerjack/services/dependency_monitor.py +52 -50
  115. crackerjack/services/enhanced_filesystem.py +14 -11
  116. crackerjack/services/file_hasher.py +1 -1
  117. crackerjack/services/filesystem.py +1 -12
  118. crackerjack/services/git.py +71 -47
  119. crackerjack/services/health_metrics.py +31 -27
  120. crackerjack/services/initialization.py +276 -428
  121. crackerjack/services/input_validator.py +760 -0
  122. crackerjack/services/log_manager.py +16 -16
  123. crackerjack/services/logging.py +7 -6
  124. crackerjack/services/metrics.py +43 -43
  125. crackerjack/services/pattern_cache.py +2 -31
  126. crackerjack/services/pattern_detector.py +26 -63
  127. crackerjack/services/performance_benchmarks.py +20 -45
  128. crackerjack/services/regex_patterns.py +2887 -0
  129. crackerjack/services/regex_utils.py +537 -0
  130. crackerjack/services/secure_path_utils.py +683 -0
  131. crackerjack/services/secure_status_formatter.py +534 -0
  132. crackerjack/services/secure_subprocess.py +605 -0
  133. crackerjack/services/security.py +47 -10
  134. crackerjack/services/security_logger.py +492 -0
  135. crackerjack/services/server_manager.py +109 -50
  136. crackerjack/services/smart_scheduling.py +8 -25
  137. crackerjack/services/status_authentication.py +603 -0
  138. crackerjack/services/status_security_manager.py +442 -0
  139. crackerjack/services/thread_safe_status_collector.py +546 -0
  140. crackerjack/services/tool_version_service.py +1 -23
  141. crackerjack/services/unified_config.py +36 -58
  142. crackerjack/services/validation_rate_limiter.py +269 -0
  143. crackerjack/services/version_checker.py +9 -40
  144. crackerjack/services/websocket_resource_limiter.py +572 -0
  145. crackerjack/slash_commands/__init__.py +52 -2
  146. crackerjack/tools/__init__.py +0 -0
  147. crackerjack/tools/validate_input_validator_patterns.py +262 -0
  148. crackerjack/tools/validate_regex_patterns.py +198 -0
  149. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/METADATA +197 -12
  150. crackerjack-0.31.13.dist-info/RECORD +178 -0
  151. crackerjack/cli/facade.py +0 -104
  152. crackerjack-0.31.10.dist-info/RECORD +0 -149
  153. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/WHEEL +0 -0
  154. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/entry_points.txt +0 -0
  155. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,454 @@
1
+ """Resource lifecycle management protocols.
2
+
3
+ Defines protocols and interfaces for comprehensive resource management
4
+ patterns throughout the crackerjack codebase.
5
+ """
6
+
7
+ import typing as t
8
+ from abc import ABC, abstractmethod
9
+ from types import TracebackType
10
+
11
+ if t.TYPE_CHECKING:
12
+ import asyncio
13
+ from pathlib import Path
14
+
15
+
16
+ class AsyncCleanupProtocol(t.Protocol):
17
+ """Protocol for resources that support async cleanup."""
18
+
19
+ async def cleanup(self) -> None:
20
+ """Clean up the resource asynchronously."""
21
+ ...
22
+
23
+
24
+ class SyncCleanupProtocol(t.Protocol):
25
+ """Protocol for resources that support synchronous cleanup."""
26
+
27
+ def cleanup(self) -> None:
28
+ """Clean up the resource synchronously."""
29
+ ...
30
+
31
+
32
+ class ResourceLifecycleProtocol(t.Protocol):
33
+ """Protocol for resources with full lifecycle management."""
34
+
35
+ async def initialize(self) -> None:
36
+ """Initialize the resource."""
37
+ ...
38
+
39
+ async def cleanup(self) -> None:
40
+ """Clean up the resource."""
41
+ ...
42
+
43
+ def is_initialized(self) -> bool:
44
+ """Check if the resource is initialized."""
45
+ ...
46
+
47
+ def is_closed(self) -> bool:
48
+ """Check if the resource is closed."""
49
+ ...
50
+
51
+
52
+ class AsyncContextProtocol(t.Protocol):
53
+ """Protocol for async context managers."""
54
+
55
+ async def __aenter__(self) -> t.Self:
56
+ """Enter async context."""
57
+ ...
58
+
59
+ async def __aexit__(
60
+ self,
61
+ exc_type: type[BaseException] | None,
62
+ exc_val: BaseException | None,
63
+ exc_tb: TracebackType | None,
64
+ ) -> None:
65
+ """Exit async context."""
66
+ ...
67
+
68
+
69
+ class ResourceManagerProtocol(t.Protocol):
70
+ """Protocol for resource managers."""
71
+
72
+ def register_resource(self, resource: AsyncCleanupProtocol) -> None:
73
+ """Register a resource for management."""
74
+ ...
75
+
76
+ def register_cleanup_callback(
77
+ self, callback: t.Callable[[], t.Awaitable[None]]
78
+ ) -> None:
79
+ """Register a cleanup callback."""
80
+ ...
81
+
82
+ async def cleanup_all(self) -> None:
83
+ """Clean up all managed resources."""
84
+ ...
85
+
86
+
87
+ class FileResourceProtocol(t.Protocol):
88
+ """Protocol for file-based resources."""
89
+
90
+ @property
91
+ def path(self) -> "Path":
92
+ """Get the file path."""
93
+ ...
94
+
95
+ def exists(self) -> bool:
96
+ """Check if the file exists."""
97
+ ...
98
+
99
+ async def cleanup(self) -> None:
100
+ """Clean up the file resource."""
101
+ ...
102
+
103
+
104
+ class ProcessResourceProtocol(t.Protocol):
105
+ """Protocol for process-based resources."""
106
+
107
+ @property
108
+ def pid(self) -> int:
109
+ """Get the process ID."""
110
+ ...
111
+
112
+ def is_running(self) -> bool:
113
+ """Check if the process is running."""
114
+ ...
115
+
116
+ async def cleanup(self) -> None:
117
+ """Clean up the process resource."""
118
+ ...
119
+
120
+
121
+ class TaskResourceProtocol(t.Protocol):
122
+ """Protocol for asyncio task resources."""
123
+
124
+ @property
125
+ def task(self) -> "asyncio.Task[t.Any]":
126
+ """Get the asyncio task."""
127
+ ...
128
+
129
+ def is_done(self) -> bool:
130
+ """Check if the task is done."""
131
+ ...
132
+
133
+ def is_cancelled(self) -> bool:
134
+ """Check if the task is cancelled."""
135
+ ...
136
+
137
+ async def cleanup(self) -> None:
138
+ """Clean up the task resource."""
139
+ ...
140
+
141
+
142
+ class NetworkResourceProtocol(t.Protocol):
143
+ """Protocol for network-based resources."""
144
+
145
+ @property
146
+ def is_connected(self) -> bool:
147
+ """Check if the resource is connected."""
148
+ ...
149
+
150
+ async def disconnect(self) -> None:
151
+ """Disconnect the resource."""
152
+ ...
153
+
154
+ async def cleanup(self) -> None:
155
+ """Clean up the network resource."""
156
+ ...
157
+
158
+
159
+ class CacheResourceProtocol(t.Protocol):
160
+ """Protocol for cache-based resources."""
161
+
162
+ def clear(self) -> None:
163
+ """Clear the cache."""
164
+ ...
165
+
166
+ def get_size(self) -> int:
167
+ """Get the cache size."""
168
+ ...
169
+
170
+ async def cleanup(self) -> None:
171
+ """Clean up the cache resource."""
172
+ ...
173
+
174
+
175
+ # Abstract base classes for resource implementations
176
+ class AbstractManagedResource(ABC):
177
+ """Abstract base class for managed resources."""
178
+
179
+ def __init__(self) -> None:
180
+ self._initialized = False
181
+ self._closed = False
182
+
183
+ @abstractmethod
184
+ async def _do_initialize(self) -> None:
185
+ """Perform resource initialization. Override in subclasses."""
186
+ pass
187
+
188
+ @abstractmethod
189
+ async def _do_cleanup(self) -> None:
190
+ """Perform resource cleanup. Override in subclasses."""
191
+ pass
192
+
193
+ async def initialize(self) -> None:
194
+ """Initialize the resource if not already initialized."""
195
+ if self._initialized:
196
+ return
197
+
198
+ try:
199
+ await self._do_initialize()
200
+ self._initialized = True
201
+ except Exception:
202
+ self._closed = True
203
+ raise
204
+
205
+ async def cleanup(self) -> None:
206
+ """Clean up the resource if not already closed."""
207
+ if self._closed:
208
+ return
209
+
210
+ self._closed = True
211
+ try:
212
+ await self._do_cleanup()
213
+ except Exception:
214
+ # Log but don't re-raise during cleanup
215
+ import logging
216
+
217
+ logging.getLogger(__name__).warning(
218
+ f"Error during cleanup of {self.__class__.__name__}", exc_info=True
219
+ )
220
+
221
+ def is_initialized(self) -> bool:
222
+ """Check if the resource is initialized."""
223
+ return self._initialized
224
+
225
+ def is_closed(self) -> bool:
226
+ """Check if the resource is closed."""
227
+ return self._closed
228
+
229
+ async def __aenter__(self) -> t.Self:
230
+ """Enter async context and initialize."""
231
+ await self.initialize()
232
+ return self
233
+
234
+ async def __aexit__(
235
+ self,
236
+ exc_type: type[BaseException] | None,
237
+ exc_val: BaseException | None,
238
+ exc_tb: TracebackType | None,
239
+ ) -> None:
240
+ """Exit async context and cleanup."""
241
+ await self.cleanup()
242
+
243
+
244
+ class AbstractFileResource(AbstractManagedResource):
245
+ """Abstract base class for file-based resources."""
246
+
247
+ def __init__(self, path: "Path") -> None:
248
+ super().__init__()
249
+ self._path = path
250
+
251
+ @property
252
+ def path(self) -> "Path":
253
+ """Get the file path."""
254
+ return self._path
255
+
256
+ def exists(self) -> bool:
257
+ """Check if the file exists."""
258
+ return self._path.exists()
259
+
260
+
261
+ class AbstractProcessResource(AbstractManagedResource):
262
+ """Abstract base class for process-based resources."""
263
+
264
+ def __init__(self, pid: int) -> None:
265
+ super().__init__()
266
+ self._pid = pid
267
+
268
+ @property
269
+ def pid(self) -> int:
270
+ """Get the process ID."""
271
+ return self._pid
272
+
273
+ def is_running(self) -> bool:
274
+ """Check if the process is running."""
275
+ try:
276
+ import os
277
+
278
+ os.kill(self._pid, 0)
279
+ return True
280
+ except (OSError, ProcessLookupError):
281
+ return False
282
+
283
+
284
+ class AbstractTaskResource(AbstractManagedResource):
285
+ """Abstract base class for asyncio task resources."""
286
+
287
+ def __init__(self, task: "asyncio.Task[t.Any]") -> None:
288
+ super().__init__()
289
+ self._task = task
290
+
291
+ @property
292
+ def task(self) -> "asyncio.Task[t.Any]":
293
+ """Get the asyncio task."""
294
+ return self._task
295
+
296
+ def is_done(self) -> bool:
297
+ """Check if the task is done."""
298
+ return self._task.done()
299
+
300
+ def is_cancelled(self) -> bool:
301
+ """Check if the task is cancelled."""
302
+ return self._task.cancelled()
303
+
304
+
305
+ class AbstractNetworkResource(AbstractManagedResource):
306
+ """Abstract base class for network-based resources."""
307
+
308
+ def __init__(self) -> None:
309
+ super().__init__()
310
+ self._connected = False
311
+
312
+ @property
313
+ def is_connected(self) -> bool:
314
+ """Check if the resource is connected."""
315
+ return self._connected and not self._closed
316
+
317
+ async def disconnect(self) -> None:
318
+ """Disconnect the resource."""
319
+ if self._connected:
320
+ self._connected = False
321
+ await self._do_disconnect()
322
+
323
+ @abstractmethod
324
+ async def _do_disconnect(self) -> None:
325
+ """Perform disconnection. Override in subclasses."""
326
+ pass
327
+
328
+ async def _do_cleanup(self) -> None:
329
+ """Cleanup includes disconnection."""
330
+ await self.disconnect()
331
+
332
+
333
+ # Resource lifecycle decorators
334
+ def with_resource_cleanup(resource_attr: str):
335
+ """Decorator to ensure resource cleanup on method exit."""
336
+
337
+ def decorator(func: t.Callable[..., t.Awaitable[t.Any]]):
338
+ async def wrapper(self: t.Any, *args: t.Any, **kwargs: t.Any) -> t.Any:
339
+ resource = getattr(self, resource_attr, None)
340
+ try:
341
+ return await func(self, *args, **kwargs)
342
+ finally:
343
+ if resource and hasattr(resource, "cleanup"):
344
+ await resource.cleanup()
345
+
346
+ return wrapper
347
+
348
+ return decorator
349
+
350
+
351
+ def ensure_initialized(resource_attr: str):
352
+ """Decorator to ensure resource is initialized before method execution."""
353
+
354
+ def decorator(func: t.Callable[..., t.Awaitable[t.Any]]):
355
+ async def wrapper(self: t.Any, *args: t.Any, **kwargs: t.Any) -> t.Any:
356
+ resource = getattr(self, resource_attr, None)
357
+ if resource and hasattr(resource, "initialize"):
358
+ await resource.initialize()
359
+ return await func(self, *args, **kwargs)
360
+
361
+ return wrapper
362
+
363
+ return decorator
364
+
365
+
366
+ # Resource health monitoring protocols
367
+ class HealthCheckProtocol(t.Protocol):
368
+ """Protocol for health checking resources."""
369
+
370
+ async def health_check(self) -> dict[str, t.Any]:
371
+ """Perform health check and return status."""
372
+ ...
373
+
374
+ def is_healthy(self) -> bool:
375
+ """Quick health check."""
376
+ ...
377
+
378
+
379
+ class MonitorableResourceProtocol(t.Protocol):
380
+ """Protocol for resources that can be monitored."""
381
+
382
+ def get_metrics(self) -> dict[str, t.Any]:
383
+ """Get resource metrics."""
384
+ ...
385
+
386
+ def get_status(self) -> str:
387
+ """Get resource status string."""
388
+ ...
389
+
390
+ async def health_check(self) -> dict[str, t.Any]:
391
+ """Perform health check."""
392
+ ...
393
+
394
+
395
+ # Resource factory protocols
396
+ class ResourceFactoryProtocol(t.Protocol):
397
+ """Protocol for resource factories."""
398
+
399
+ async def create_resource(self, **kwargs: t.Any) -> AsyncCleanupProtocol:
400
+ """Create a new resource instance."""
401
+ ...
402
+
403
+ def get_resource_type(self) -> str:
404
+ """Get the resource type name."""
405
+ ...
406
+
407
+
408
+ class PooledResourceProtocol(t.Protocol):
409
+ """Protocol for pooled resources."""
410
+
411
+ async def acquire(self) -> AsyncCleanupProtocol:
412
+ """Acquire a resource from the pool."""
413
+ ...
414
+
415
+ async def release(self, resource: AsyncCleanupProtocol) -> None:
416
+ """Release a resource back to the pool."""
417
+ ...
418
+
419
+ def get_pool_size(self) -> int:
420
+ """Get current pool size."""
421
+ ...
422
+
423
+ def get_active_count(self) -> int:
424
+ """Get count of active resources."""
425
+ ...
426
+
427
+
428
+ # Error handling protocols
429
+ class ResourceErrorProtocol(t.Protocol):
430
+ """Protocol for resource error handling."""
431
+
432
+ def handle_error(self, error: Exception) -> bool:
433
+ """Handle resource error. Return True if error was handled."""
434
+ ...
435
+
436
+ def should_retry(self, error: Exception) -> bool:
437
+ """Check if operation should be retried on this error."""
438
+ ...
439
+
440
+ def get_retry_delay(self, attempt: int) -> float:
441
+ """Get delay before retry attempt."""
442
+ ...
443
+
444
+
445
+ class FallbackResourceProtocol(t.Protocol):
446
+ """Protocol for resources with fallback capabilities."""
447
+
448
+ async def get_fallback(self) -> AsyncCleanupProtocol | None:
449
+ """Get fallback resource if primary fails."""
450
+ ...
451
+
452
+ def has_fallback(self) -> bool:
453
+ """Check if fallback is available."""
454
+ ...
@@ -95,7 +95,7 @@ class SessionTracker(BaseModel, arbitrary_types_allowed=True):
95
95
  )
96
96
  self.tasks[task_id] = task
97
97
  self.current_task = task_id
98
- self.console.print(f"[yellow]⏳[/yellow] Started: {task_name}")
98
+ self.console.print(f"[yellow]⏳[/ yellow] Started: {task_name}")
99
99
 
100
100
  def complete_task(
101
101
  self,
@@ -112,7 +112,7 @@ class SessionTracker(BaseModel, arbitrary_types_allowed=True):
112
112
  task.details = details
113
113
  if files_changed:
114
114
  task.files_changed = files_changed
115
- self.console.print(f"[green]✅[/green] Completed: {task.name}")
115
+ self.console.print(f"[green]✅[/ green] Completed: {task.name}")
116
116
  if self.current_task == task_id:
117
117
  self.current_task = None
118
118
 
@@ -131,7 +131,7 @@ class SessionTracker(BaseModel, arbitrary_types_allowed=True):
131
131
  if details:
132
132
  task.details = details
133
133
  self.console.print(
134
- f"[red]❌[/red] Failed: {task.name} - {error_message}",
134
+ f"[red]❌[/ red] Failed: {task.name} - {error_message}",
135
135
  )
136
136
  if self.current_task == task_id:
137
137
  self.current_task = None
File without changes