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.
- crackerjack/CLAUDE.md +1005 -0
- crackerjack/RULES.md +380 -0
- crackerjack/__init__.py +42 -13
- crackerjack/__main__.py +225 -253
- crackerjack/agents/__init__.py +41 -0
- crackerjack/agents/architect_agent.py +281 -0
- crackerjack/agents/base.py +169 -0
- crackerjack/agents/coordinator.py +512 -0
- crackerjack/agents/documentation_agent.py +498 -0
- crackerjack/agents/dry_agent.py +388 -0
- crackerjack/agents/formatting_agent.py +245 -0
- crackerjack/agents/import_optimization_agent.py +281 -0
- crackerjack/agents/performance_agent.py +669 -0
- crackerjack/agents/proactive_agent.py +104 -0
- crackerjack/agents/refactoring_agent.py +788 -0
- crackerjack/agents/security_agent.py +529 -0
- crackerjack/agents/test_creation_agent.py +652 -0
- crackerjack/agents/test_specialist_agent.py +486 -0
- crackerjack/agents/tracker.py +212 -0
- crackerjack/api.py +560 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/facade.py +104 -0
- crackerjack/cli/handlers.py +267 -0
- crackerjack/cli/interactive.py +471 -0
- crackerjack/cli/options.py +401 -0
- crackerjack/cli/utils.py +18 -0
- crackerjack/code_cleaner.py +670 -0
- crackerjack/config/__init__.py +19 -0
- crackerjack/config/hooks.py +218 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +406 -0
- crackerjack/core/autofix_coordinator.py +200 -0
- crackerjack/core/container.py +104 -0
- crackerjack/core/enhanced_container.py +542 -0
- crackerjack/core/performance.py +243 -0
- crackerjack/core/phase_coordinator.py +561 -0
- crackerjack/core/proactive_workflow.py +316 -0
- crackerjack/core/session_coordinator.py +289 -0
- crackerjack/core/workflow_orchestrator.py +640 -0
- crackerjack/dynamic_config.py +577 -0
- crackerjack/errors.py +263 -41
- crackerjack/executors/__init__.py +11 -0
- crackerjack/executors/async_hook_executor.py +431 -0
- crackerjack/executors/cached_hook_executor.py +242 -0
- crackerjack/executors/hook_executor.py +345 -0
- crackerjack/executors/individual_hook_executor.py +669 -0
- crackerjack/intelligence/__init__.py +44 -0
- crackerjack/intelligence/adaptive_learning.py +751 -0
- crackerjack/intelligence/agent_orchestrator.py +551 -0
- crackerjack/intelligence/agent_registry.py +414 -0
- crackerjack/intelligence/agent_selector.py +502 -0
- crackerjack/intelligence/integration.py +290 -0
- crackerjack/interactive.py +576 -315
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +137 -0
- crackerjack/managers/publish_manager.py +411 -0
- crackerjack/managers/test_command_builder.py +151 -0
- crackerjack/managers/test_executor.py +435 -0
- crackerjack/managers/test_manager.py +258 -0
- crackerjack/managers/test_manager_backup.py +1124 -0
- crackerjack/managers/test_progress.py +144 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +336 -0
- crackerjack/mcp/client_runner.py +104 -0
- crackerjack/mcp/context.py +615 -0
- crackerjack/mcp/dashboard.py +636 -0
- crackerjack/mcp/enhanced_progress_monitor.py +479 -0
- crackerjack/mcp/file_monitor.py +336 -0
- crackerjack/mcp/progress_components.py +569 -0
- crackerjack/mcp/progress_monitor.py +949 -0
- crackerjack/mcp/rate_limiter.py +332 -0
- crackerjack/mcp/server.py +22 -0
- crackerjack/mcp/server_core.py +244 -0
- crackerjack/mcp/service_watchdog.py +501 -0
- crackerjack/mcp/state.py +395 -0
- crackerjack/mcp/task_manager.py +257 -0
- crackerjack/mcp/tools/__init__.py +17 -0
- crackerjack/mcp/tools/core_tools.py +249 -0
- crackerjack/mcp/tools/error_analyzer.py +308 -0
- crackerjack/mcp/tools/execution_tools.py +370 -0
- crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
- crackerjack/mcp/tools/intelligence_tools.py +314 -0
- crackerjack/mcp/tools/monitoring_tools.py +502 -0
- crackerjack/mcp/tools/proactive_tools.py +384 -0
- crackerjack/mcp/tools/progress_tools.py +141 -0
- crackerjack/mcp/tools/utility_tools.py +341 -0
- crackerjack/mcp/tools/workflow_executor.py +360 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +39 -0
- crackerjack/mcp/websocket/endpoints.py +559 -0
- crackerjack/mcp/websocket/jobs.py +253 -0
- crackerjack/mcp/websocket/server.py +116 -0
- crackerjack/mcp/websocket/websocket_handler.py +78 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/__init__.py +31 -0
- crackerjack/models/config.py +93 -0
- crackerjack/models/config_adapter.py +230 -0
- crackerjack/models/protocols.py +118 -0
- crackerjack/models/task.py +154 -0
- crackerjack/monitoring/ai_agent_watchdog.py +450 -0
- crackerjack/monitoring/regression_prevention.py +638 -0
- crackerjack/orchestration/__init__.py +0 -0
- crackerjack/orchestration/advanced_orchestrator.py +970 -0
- crackerjack/orchestration/execution_strategies.py +341 -0
- crackerjack/orchestration/test_progress_streamer.py +636 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +246 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +259 -0
- crackerjack/py313.py +8 -3
- crackerjack/services/__init__.py +22 -0
- crackerjack/services/cache.py +314 -0
- crackerjack/services/config.py +347 -0
- crackerjack/services/config_integrity.py +99 -0
- crackerjack/services/contextual_ai_assistant.py +516 -0
- crackerjack/services/coverage_ratchet.py +347 -0
- crackerjack/services/debug.py +736 -0
- crackerjack/services/dependency_monitor.py +617 -0
- crackerjack/services/enhanced_filesystem.py +439 -0
- crackerjack/services/file_hasher.py +151 -0
- crackerjack/services/filesystem.py +395 -0
- crackerjack/services/git.py +165 -0
- crackerjack/services/health_metrics.py +611 -0
- crackerjack/services/initialization.py +847 -0
- crackerjack/services/log_manager.py +286 -0
- crackerjack/services/logging.py +174 -0
- crackerjack/services/metrics.py +578 -0
- crackerjack/services/pattern_cache.py +362 -0
- crackerjack/services/pattern_detector.py +515 -0
- crackerjack/services/performance_benchmarks.py +653 -0
- crackerjack/services/security.py +163 -0
- crackerjack/services/server_manager.py +234 -0
- crackerjack/services/smart_scheduling.py +144 -0
- crackerjack/services/tool_version_service.py +61 -0
- crackerjack/services/unified_config.py +437 -0
- crackerjack/services/version_checker.py +248 -0
- crackerjack/slash_commands/__init__.py +14 -0
- crackerjack/slash_commands/init.md +122 -0
- crackerjack/slash_commands/run.md +163 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack-0.31.4.dist-info/METADATA +742 -0
- crackerjack-0.31.4.dist-info/RECORD +148 -0
- crackerjack-0.31.4.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -34
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config-ai.yaml +0 -149
- crackerjack/.pre-commit-config-fast.yaml +0 -69
- crackerjack/.pre-commit-config.yaml +0 -114
- crackerjack/crackerjack.py +0 -4140
- crackerjack/pyproject.toml +0 -285
- crackerjack-0.29.0.dist-info/METADATA +0 -1289
- crackerjack-0.29.0.dist-info/RECORD +0 -17
- {crackerjack-0.29.0.dist-info → crackerjack-0.31.4.dist-info}/WHEEL +0 -0
- {crackerjack-0.29.0.dist-info → crackerjack-0.31.4.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
|
+
}
|