crackerjack 0.30.3__py3-none-any.whl → 0.31.7__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 (156) hide show
  1. crackerjack/CLAUDE.md +1005 -0
  2. crackerjack/RULES.md +380 -0
  3. crackerjack/__init__.py +42 -13
  4. crackerjack/__main__.py +227 -299
  5. crackerjack/agents/__init__.py +41 -0
  6. crackerjack/agents/architect_agent.py +281 -0
  7. crackerjack/agents/base.py +170 -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 +657 -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 +409 -0
  26. crackerjack/cli/utils.py +18 -0
  27. crackerjack/code_cleaner.py +618 -928
  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 +585 -0
  37. crackerjack/core/proactive_workflow.py +316 -0
  38. crackerjack/core/session_coordinator.py +289 -0
  39. crackerjack/core/workflow_orchestrator.py +826 -0
  40. crackerjack/dynamic_config.py +94 -103
  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 +433 -0
  58. crackerjack/managers/test_command_builder.py +151 -0
  59. crackerjack/managers/test_executor.py +443 -0
  60. crackerjack/managers/test_manager.py +258 -0
  61. crackerjack/managers/test_manager_backup.py +1124 -0
  62. crackerjack/managers/test_progress.py +114 -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 +621 -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 +372 -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 +217 -0
  88. crackerjack/mcp/tools/utility_tools.py +341 -0
  89. crackerjack/mcp/tools/workflow_executor.py +565 -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/coverage_improvement.py +223 -0
  107. crackerjack/orchestration/execution_strategies.py +341 -0
  108. crackerjack/orchestration/test_progress_streamer.py +636 -0
  109. crackerjack/plugins/__init__.py +15 -0
  110. crackerjack/plugins/base.py +200 -0
  111. crackerjack/plugins/hooks.py +246 -0
  112. crackerjack/plugins/loader.py +335 -0
  113. crackerjack/plugins/managers.py +259 -0
  114. crackerjack/py313.py +8 -3
  115. crackerjack/services/__init__.py +22 -0
  116. crackerjack/services/cache.py +314 -0
  117. crackerjack/services/config.py +358 -0
  118. crackerjack/services/config_integrity.py +99 -0
  119. crackerjack/services/contextual_ai_assistant.py +516 -0
  120. crackerjack/services/coverage_ratchet.py +356 -0
  121. crackerjack/services/debug.py +736 -0
  122. crackerjack/services/dependency_monitor.py +617 -0
  123. crackerjack/services/enhanced_filesystem.py +439 -0
  124. crackerjack/services/file_hasher.py +151 -0
  125. crackerjack/services/filesystem.py +421 -0
  126. crackerjack/services/git.py +176 -0
  127. crackerjack/services/health_metrics.py +611 -0
  128. crackerjack/services/initialization.py +873 -0
  129. crackerjack/services/log_manager.py +286 -0
  130. crackerjack/services/logging.py +174 -0
  131. crackerjack/services/metrics.py +578 -0
  132. crackerjack/services/pattern_cache.py +362 -0
  133. crackerjack/services/pattern_detector.py +515 -0
  134. crackerjack/services/performance_benchmarks.py +653 -0
  135. crackerjack/services/security.py +163 -0
  136. crackerjack/services/server_manager.py +234 -0
  137. crackerjack/services/smart_scheduling.py +144 -0
  138. crackerjack/services/tool_version_service.py +61 -0
  139. crackerjack/services/unified_config.py +437 -0
  140. crackerjack/services/version_checker.py +248 -0
  141. crackerjack/slash_commands/__init__.py +14 -0
  142. crackerjack/slash_commands/init.md +122 -0
  143. crackerjack/slash_commands/run.md +163 -0
  144. crackerjack/slash_commands/status.md +127 -0
  145. crackerjack-0.31.7.dist-info/METADATA +742 -0
  146. crackerjack-0.31.7.dist-info/RECORD +149 -0
  147. crackerjack-0.31.7.dist-info/entry_points.txt +2 -0
  148. crackerjack/.gitignore +0 -34
  149. crackerjack/.libcst.codemod.yaml +0 -18
  150. crackerjack/.pdm.toml +0 -1
  151. crackerjack/crackerjack.py +0 -3805
  152. crackerjack/pyproject.toml +0 -286
  153. crackerjack-0.30.3.dist-info/METADATA +0 -1290
  154. crackerjack-0.30.3.dist-info/RECORD +0 -16
  155. {crackerjack-0.30.3.dist-info → crackerjack-0.31.7.dist-info}/WHEEL +0 -0
  156. {crackerjack-0.30.3.dist-info → crackerjack-0.31.7.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,230 @@
1
+ """Adapter for converting between the old OptionsProtocol and new WorkflowOptions.
2
+
3
+ This provides backward compatibility during the migration period.
4
+ """
5
+
6
+ import typing as t
7
+
8
+ from .config import (
9
+ AIConfig,
10
+ CleaningConfig,
11
+ EnterpriseConfig,
12
+ ExecutionConfig,
13
+ GitConfig,
14
+ HookConfig,
15
+ ProgressConfig,
16
+ PublishConfig,
17
+ TestConfig,
18
+ WorkflowOptions,
19
+ )
20
+ from .protocols import OptionsProtocol
21
+
22
+
23
+ class OptionsAdapter:
24
+ """Adapter to convert between old and new configuration formats."""
25
+
26
+ @staticmethod
27
+ def from_options_protocol(options: OptionsProtocol) -> WorkflowOptions:
28
+ """Convert OptionsProtocol to WorkflowOptions."""
29
+ return WorkflowOptions(
30
+ cleaning=CleaningConfig(
31
+ clean=getattr(options, "clean", True),
32
+ update_docs=getattr(options, "update_docs", False),
33
+ force_update_docs=getattr(options, "force_update_docs", False),
34
+ compress_docs=getattr(options, "compress_docs", False),
35
+ auto_compress_docs=getattr(options, "auto_compress_docs", False),
36
+ ),
37
+ hooks=HookConfig(
38
+ skip_hooks=getattr(options, "skip_hooks", False),
39
+ update_precommit=getattr(options, "update_precommit", False),
40
+ experimental_hooks=getattr(options, "experimental_hooks", False),
41
+ enable_pyrefly=getattr(options, "enable_pyrefly", False),
42
+ enable_ty=getattr(options, "enable_ty", False),
43
+ ),
44
+ testing=TestConfig(
45
+ test=getattr(options, "test", False),
46
+ benchmark=getattr(options, "benchmark", False),
47
+ test_workers=getattr(options, "test_workers", 0),
48
+ test_timeout=getattr(options, "test_timeout", 0),
49
+ ),
50
+ publishing=PublishConfig(
51
+ publish=getattr(options, "publish", None),
52
+ bump=getattr(options, "bump", None),
53
+ all=getattr(options, "all", None),
54
+ no_git_tags=getattr(options, "no_git_tags", False),
55
+ skip_version_check=getattr(options, "skip_version_check", False),
56
+ ),
57
+ git=GitConfig(
58
+ commit=getattr(options, "commit", False),
59
+ create_pr=getattr(options, "create_pr", False),
60
+ ),
61
+ ai=AIConfig(
62
+ ai_agent=getattr(options, "ai_agent", False),
63
+ autofix=getattr(options, "autofix", True), # Default true per CLAUDE.md
64
+ ai_agent_autofix=getattr(options, "ai_agent_autofix", False),
65
+ start_mcp_server=getattr(options, "start_mcp_server", False),
66
+ max_iterations=getattr(options, "max_iterations", 10),
67
+ ),
68
+ execution=ExecutionConfig(
69
+ interactive=getattr(options, "interactive", False),
70
+ verbose=getattr(options, "verbose", False),
71
+ async_mode=getattr(options, "async_mode", False),
72
+ no_config_updates=getattr(options, "no_config_updates", False),
73
+ ),
74
+ progress=ProgressConfig(
75
+ enabled=getattr(options, "track_progress", False),
76
+ ),
77
+ enterprise=EnterpriseConfig(
78
+ enabled=getattr(options, "enterprise_batch", None) is not None,
79
+ license_key=getattr(options, "license_key", None),
80
+ organization=getattr(options, "organization", None),
81
+ ),
82
+ )
83
+
84
+ @staticmethod
85
+ def to_options_protocol(
86
+ workflow_options: WorkflowOptions,
87
+ ) -> "LegacyOptionsWrapper":
88
+ """Convert WorkflowOptions back to OptionsProtocol for backward compatibility."""
89
+ return LegacyOptionsWrapper(workflow_options)
90
+
91
+
92
+ class LegacyOptionsWrapper:
93
+ """Wrapper that provides OptionsProtocol interface over WorkflowOptions."""
94
+
95
+ def __init__(self, workflow_options: WorkflowOptions) -> None:
96
+ self._options = workflow_options
97
+
98
+ # All the original OptionsProtocol attributes, delegating to the focused configs
99
+
100
+ @property
101
+ def commit(self) -> bool:
102
+ return self._options.git.commit
103
+
104
+ @property
105
+ def interactive(self) -> bool:
106
+ return self._options.execution.interactive
107
+
108
+ @property
109
+ def no_config_updates(self) -> bool:
110
+ return self._options.execution.no_config_updates
111
+
112
+ @property
113
+ def verbose(self) -> bool:
114
+ return self._options.execution.verbose
115
+
116
+ @property
117
+ def update_docs(self) -> bool:
118
+ return self._options.cleaning.update_docs
119
+
120
+ @property
121
+ def force_update_docs(self) -> bool:
122
+ return self._options.cleaning.force_update_docs
123
+
124
+ @property
125
+ def compress_docs(self) -> bool:
126
+ return self._options.cleaning.compress_docs
127
+
128
+ @property
129
+ def auto_compress_docs(self) -> bool:
130
+ return self._options.cleaning.auto_compress_docs
131
+
132
+ @property
133
+ def clean(self) -> bool:
134
+ return self._options.cleaning.clean
135
+
136
+ @property
137
+ def test(self) -> bool:
138
+ return self._options.testing.test
139
+
140
+ @property
141
+ def benchmark(self) -> bool:
142
+ return self._options.testing.benchmark
143
+
144
+ @property
145
+ def test_workers(self) -> int:
146
+ return self._options.testing.test_workers
147
+
148
+ @property
149
+ def test_timeout(self) -> int:
150
+ return self._options.testing.test_timeout
151
+
152
+ @property
153
+ def publish(self) -> t.Any | None:
154
+ return self._options.publishing.publish
155
+
156
+ @property
157
+ def bump(self) -> t.Any | None:
158
+ return self._options.publishing.bump
159
+
160
+ @property
161
+ def all(self) -> t.Any | None:
162
+ return self._options.publishing.all
163
+
164
+ @property
165
+ def ai_agent(self) -> bool:
166
+ return self._options.ai.ai_agent
167
+
168
+ @property
169
+ def autofix(self) -> bool:
170
+ return self._options.ai.autofix
171
+
172
+ @property
173
+ def ai_agent_autofix(self) -> bool:
174
+ return self._options.ai.ai_agent_autofix
175
+
176
+ @property
177
+ def start_mcp_server(self) -> bool:
178
+ return self._options.ai.start_mcp_server
179
+
180
+ @property
181
+ def max_iterations(self) -> int:
182
+ return self._options.ai.max_iterations
183
+
184
+ @property
185
+ def create_pr(self) -> bool:
186
+ return self._options.git.create_pr
187
+
188
+ @property
189
+ def skip_hooks(self) -> bool:
190
+ return self._options.hooks.skip_hooks
191
+
192
+ @property
193
+ def update_precommit(self) -> bool:
194
+ return self._options.hooks.update_precommit
195
+
196
+ @property
197
+ def async_mode(self) -> bool:
198
+ return self._options.execution.async_mode
199
+
200
+ @property
201
+ def track_progress(self) -> bool:
202
+ return self._options.progress.enabled
203
+
204
+ @property
205
+ def experimental_hooks(self) -> bool:
206
+ return self._options.hooks.experimental_hooks
207
+
208
+ @property
209
+ def enable_pyrefly(self) -> bool:
210
+ return self._options.hooks.enable_pyrefly
211
+
212
+ @property
213
+ def enable_ty(self) -> bool:
214
+ return self._options.hooks.enable_ty
215
+
216
+ @property
217
+ def no_git_tags(self) -> bool:
218
+ return self._options.publishing.no_git_tags
219
+
220
+ @property
221
+ def skip_version_check(self) -> bool:
222
+ return self._options.publishing.skip_version_check
223
+
224
+ @property
225
+ def enterprise_batch(self) -> str | None:
226
+ return None # Deprecated field
227
+
228
+ @property
229
+ def monitor_dashboard(self) -> str | None:
230
+ return None # Deprecated field
@@ -0,0 +1,118 @@
1
+ import subprocess
2
+ import typing as t
3
+
4
+
5
+ @t.runtime_checkable
6
+ class CommandRunner(t.Protocol):
7
+ def execute_command(
8
+ self,
9
+ cmd: list[str],
10
+ **kwargs: t.Any,
11
+ ) -> subprocess.CompletedProcess[str]: ...
12
+
13
+
14
+ @t.runtime_checkable
15
+ class OptionsProtocol(t.Protocol):
16
+ commit: bool
17
+ interactive: bool
18
+ no_config_updates: bool
19
+ verbose: bool
20
+ clean: bool
21
+ test: bool
22
+ benchmark: bool
23
+ test_workers: int = 0
24
+ test_timeout: int = 0
25
+ publish: t.Any | None
26
+ bump: t.Any | None
27
+ all: t.Any | None
28
+ ai_agent: bool = False
29
+ start_mcp_server: bool = False
30
+ create_pr: bool = False
31
+ skip_hooks: bool = False
32
+ update_precommit: bool = False
33
+ async_mode: bool = False
34
+ experimental_hooks: bool = False
35
+ enable_pyrefly: bool = False
36
+ enable_ty: bool = False
37
+ cleanup: t.Any | None = None
38
+ no_git_tags: bool = False
39
+ skip_version_check: bool = False
40
+ cleanup_pypi: bool = False
41
+ coverage: bool = False
42
+ keep_releases: int = 10
43
+ track_progress: bool = False
44
+ fast: bool = False
45
+ comp: bool = False
46
+ enterprise_batch: str | None = None
47
+ monitor_dashboard: str | None = None
48
+ coverage: bool = False
49
+
50
+
51
+ @t.runtime_checkable
52
+ class ConsoleInterface(t.Protocol):
53
+ def print(self, *args: t.Any, **kwargs: t.Any) -> None: ...
54
+
55
+ def input(self, _: str = "") -> str: ...
56
+
57
+
58
+ @t.runtime_checkable
59
+ class FileSystemInterface(t.Protocol):
60
+ def read_file(self, path: str | t.Any) -> str: ...
61
+
62
+ def write_file(self, path: str | t.Any, content: str) -> None: ...
63
+
64
+ def exists(self, path: str | t.Any) -> bool: ...
65
+
66
+ def mkdir(self, path: str | t.Any, parents: bool = False) -> None: ...
67
+
68
+
69
+ @t.runtime_checkable
70
+ class GitInterface(t.Protocol):
71
+ def is_git_repo(self) -> bool: ...
72
+
73
+ def get_changed_files(self) -> list[str]: ...
74
+
75
+ def commit(self, message: str) -> bool: ...
76
+
77
+ def push(self) -> bool: ...
78
+
79
+ def add_files(self, files: list[str]) -> bool: ...
80
+
81
+ def get_commit_message_suggestions(self, changed_files: list[str]) -> list[str]: ...
82
+
83
+
84
+ @t.runtime_checkable
85
+ class HookManager(t.Protocol):
86
+ def run_fast_hooks(self) -> list[t.Any]: ...
87
+
88
+ def run_comprehensive_hooks(self) -> list[t.Any]: ...
89
+
90
+ def install_hooks(self) -> bool: ...
91
+
92
+ def set_config_path(self, path: str | t.Any) -> None: ...
93
+
94
+ def get_hook_summary(self, results: t.Any) -> t.Any: ...
95
+
96
+
97
+ @t.runtime_checkable
98
+ class TestManagerProtocol(t.Protocol):
99
+ def run_tests(self, options: OptionsProtocol) -> bool: ...
100
+
101
+ def get_coverage(self) -> dict[str, t.Any]: ...
102
+
103
+ def validate_test_environment(self) -> bool: ...
104
+
105
+ def get_test_failures(self) -> list[str]: ...
106
+
107
+
108
+ @t.runtime_checkable
109
+ class PublishManager(t.Protocol):
110
+ def bump_version(self, version_type: str) -> str: ...
111
+
112
+ def publish_package(self) -> bool: ...
113
+
114
+ def validate_auth(self) -> bool: ...
115
+
116
+ def create_git_tag(self, version: str) -> bool: ...
117
+
118
+ def cleanup_old_releases(self, keep_releases: int) -> None: ...
@@ -0,0 +1,154 @@
1
+ import time
2
+ import typing as t
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+
6
+ from pydantic import BaseModel
7
+ from rich.console import Console
8
+
9
+
10
+ class TaskStatus(Enum):
11
+ PENDING = "pending"
12
+ IN_PROGRESS = "in_progress"
13
+ COMPLETED = "completed"
14
+ FAILED = "failed"
15
+
16
+
17
+ @dataclass
18
+ class Task:
19
+ id: str
20
+ name: str
21
+ status: TaskStatus
22
+ details: str | None = None
23
+
24
+ def to_dict(self) -> dict[str, t.Any]:
25
+ return {
26
+ "id": self.id,
27
+ "name": self.name,
28
+ "status": self.status.value,
29
+ "details": self.details,
30
+ }
31
+
32
+
33
+ @dataclass
34
+ class HookResult:
35
+ id: str
36
+ name: str
37
+ status: str
38
+ duration: float
39
+ files_processed: int = 0
40
+ issues_found: list[str] | None = None
41
+ stage: str = "pre-commit"
42
+
43
+ def __post_init__(self) -> None:
44
+ if self.issues_found is None:
45
+ self.issues_found = []
46
+
47
+
48
+ @dataclass
49
+ class TaskStatusData:
50
+ id: str
51
+ name: str
52
+ status: str
53
+ start_time: float | None = None
54
+ end_time: float | None = None
55
+ duration: float | None = None
56
+ details: str | None = None
57
+ error_message: str | None = None
58
+ files_changed: list[str] | None = None
59
+
60
+ def __post_init__(self) -> None:
61
+ if self.files_changed is None:
62
+ self.files_changed = []
63
+ if self.start_time is not None and self.end_time is not None:
64
+ self.duration = self.end_time - self.start_time
65
+
66
+
67
+ class SessionTracker(BaseModel, arbitrary_types_allowed=True):
68
+ console: Console
69
+ session_id: str
70
+ start_time: float
71
+ progress_file: t.Any = None
72
+ tasks: dict[str, TaskStatusData] = {}
73
+ current_task: str | None = None
74
+ metadata: dict[str, t.Any] = {}
75
+
76
+ def __init__(self, **data: t.Any) -> None:
77
+ super().__init__(**data)
78
+ if not self.tasks:
79
+ self.tasks = {}
80
+ if not self.metadata:
81
+ self.metadata = {}
82
+
83
+ def start_task(
84
+ self,
85
+ task_id: str,
86
+ task_name: str,
87
+ details: str | None = None,
88
+ ) -> None:
89
+ task = TaskStatusData(
90
+ id=task_id,
91
+ name=task_name,
92
+ status="in_progress",
93
+ start_time=time.time(),
94
+ details=details,
95
+ )
96
+ self.tasks[task_id] = task
97
+ self.current_task = task_id
98
+ self.console.print(f"[yellow]⏳[/yellow] Started: {task_name}")
99
+
100
+ def complete_task(
101
+ self,
102
+ task_id: str,
103
+ details: str | None = None,
104
+ files_changed: list[str] | None = None,
105
+ ) -> None:
106
+ if task_id in self.tasks:
107
+ task = self.tasks[task_id]
108
+ task.status = "completed"
109
+ task.end_time = time.time()
110
+ task.duration = task.end_time - (task.start_time or task.end_time)
111
+ if details:
112
+ task.details = details
113
+ if files_changed:
114
+ task.files_changed = files_changed
115
+ self.console.print(f"[green]✅[/green] Completed: {task.name}")
116
+ if self.current_task == task_id:
117
+ self.current_task = None
118
+
119
+ def fail_task(
120
+ self,
121
+ task_id: str,
122
+ error_message: str,
123
+ details: str | None = None,
124
+ ) -> None:
125
+ if task_id in self.tasks:
126
+ task = self.tasks[task_id]
127
+ task.status = "failed"
128
+ task.end_time = time.time()
129
+ task.duration = task.end_time - (task.start_time or task.end_time)
130
+ task.error_message = error_message
131
+ if details:
132
+ task.details = details
133
+ self.console.print(
134
+ f"[red]❌[/red] Failed: {task.name} - {error_message}",
135
+ )
136
+ if self.current_task == task_id:
137
+ self.current_task = None
138
+
139
+ def get_summary(self) -> dict[str, t.Any]:
140
+ completed = sum(1 for task in self.tasks.values() if task.status == "completed")
141
+ failed = sum(1 for task in self.tasks.values() if task.status == "failed")
142
+ in_progress = sum(
143
+ 1 for task in self.tasks.values() if task.status == "in_progress"
144
+ )
145
+
146
+ return {
147
+ "session_id": self.session_id,
148
+ "duration": time.time() - self.start_time,
149
+ "total_tasks": len(self.tasks),
150
+ "completed": completed,
151
+ "failed": failed,
152
+ "in_progress": in_progress,
153
+ "current_task": self.current_task,
154
+ }