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,218 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HookStage(Enum):
|
|
7
|
+
FAST = "fast"
|
|
8
|
+
COMPREHENSIVE = "comprehensive"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RetryPolicy(Enum):
|
|
12
|
+
NONE = "none"
|
|
13
|
+
FORMATTING_ONLY = "formatting_only"
|
|
14
|
+
ALL_HOOKS = "all_hooks"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class HookDefinition:
|
|
19
|
+
name: str
|
|
20
|
+
command: list[str]
|
|
21
|
+
timeout: int = 60
|
|
22
|
+
stage: HookStage = HookStage.FAST
|
|
23
|
+
retry_on_failure: bool = False
|
|
24
|
+
is_formatting: bool = False
|
|
25
|
+
manual_stage: bool = False
|
|
26
|
+
config_path: Path | None = None
|
|
27
|
+
|
|
28
|
+
def get_command(self) -> list[str]:
|
|
29
|
+
cmd = ["pre-commit", "run"]
|
|
30
|
+
if self.config_path:
|
|
31
|
+
cmd.extend(["-c", str(self.config_path)])
|
|
32
|
+
if self.manual_stage:
|
|
33
|
+
cmd.extend(["--hook-stage", "manual"])
|
|
34
|
+
cmd.extend([self.name, "--all-files"])
|
|
35
|
+
return cmd
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class HookStrategy:
|
|
40
|
+
name: str
|
|
41
|
+
hooks: list[HookDefinition]
|
|
42
|
+
timeout: int = 300
|
|
43
|
+
retry_policy: RetryPolicy = RetryPolicy.NONE
|
|
44
|
+
parallel: bool = False
|
|
45
|
+
max_workers: int = 3
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
FAST_HOOKS = [
|
|
49
|
+
HookDefinition(
|
|
50
|
+
name="trailing-whitespace",
|
|
51
|
+
command=["pre-commit", "run", "trailing-whitespace", "--all-files"],
|
|
52
|
+
is_formatting=True,
|
|
53
|
+
retry_on_failure=True,
|
|
54
|
+
),
|
|
55
|
+
HookDefinition(
|
|
56
|
+
name="end-of-file-fixer",
|
|
57
|
+
command=["pre-commit", "run", "end-of-file-fixer", "--all-files"],
|
|
58
|
+
is_formatting=True,
|
|
59
|
+
retry_on_failure=True,
|
|
60
|
+
),
|
|
61
|
+
HookDefinition(
|
|
62
|
+
name="check-yaml",
|
|
63
|
+
command=["pre-commit", "run", "check-yaml", "--all-files"],
|
|
64
|
+
),
|
|
65
|
+
HookDefinition(
|
|
66
|
+
name="check-toml",
|
|
67
|
+
command=["pre-commit", "run", "check-toml", "--all-files"],
|
|
68
|
+
),
|
|
69
|
+
HookDefinition(
|
|
70
|
+
name="check-added-large-files",
|
|
71
|
+
command=["pre-commit", "run", "check-added-large-files", "--all-files"],
|
|
72
|
+
),
|
|
73
|
+
HookDefinition(
|
|
74
|
+
name="uv-lock",
|
|
75
|
+
command=["pre-commit", "run", "uv-lock", "--all-files"],
|
|
76
|
+
),
|
|
77
|
+
HookDefinition(
|
|
78
|
+
name="gitleaks",
|
|
79
|
+
command=["pre-commit", "run", "gitleaks", "--all-files"],
|
|
80
|
+
),
|
|
81
|
+
HookDefinition(
|
|
82
|
+
name="codespell",
|
|
83
|
+
command=["pre-commit", "run", "codespell", "--all-files"],
|
|
84
|
+
),
|
|
85
|
+
HookDefinition(
|
|
86
|
+
name="ruff-check",
|
|
87
|
+
command=["pre-commit", "run", "ruff-check", "--all-files"],
|
|
88
|
+
),
|
|
89
|
+
HookDefinition(
|
|
90
|
+
name="ruff-format",
|
|
91
|
+
command=["pre-commit", "run", "ruff-format", "--all-files"],
|
|
92
|
+
is_formatting=True,
|
|
93
|
+
retry_on_failure=True,
|
|
94
|
+
),
|
|
95
|
+
HookDefinition(
|
|
96
|
+
name="mdformat",
|
|
97
|
+
command=["pre-commit", "run", "mdformat", "--all-files"],
|
|
98
|
+
is_formatting=True,
|
|
99
|
+
retry_on_failure=True,
|
|
100
|
+
),
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
COMPREHENSIVE_HOOKS = [
|
|
104
|
+
HookDefinition(
|
|
105
|
+
name="pyright",
|
|
106
|
+
command=[
|
|
107
|
+
"pre-commit",
|
|
108
|
+
"run",
|
|
109
|
+
"--hook-stage",
|
|
110
|
+
"manual",
|
|
111
|
+
"pyright",
|
|
112
|
+
"--all-files",
|
|
113
|
+
],
|
|
114
|
+
timeout=120,
|
|
115
|
+
stage=HookStage.COMPREHENSIVE,
|
|
116
|
+
manual_stage=True,
|
|
117
|
+
),
|
|
118
|
+
HookDefinition(
|
|
119
|
+
name="bandit",
|
|
120
|
+
command=[
|
|
121
|
+
"pre-commit",
|
|
122
|
+
"run",
|
|
123
|
+
"--hook-stage",
|
|
124
|
+
"manual",
|
|
125
|
+
"bandit",
|
|
126
|
+
"--all-files",
|
|
127
|
+
],
|
|
128
|
+
timeout=120,
|
|
129
|
+
stage=HookStage.COMPREHENSIVE,
|
|
130
|
+
manual_stage=True,
|
|
131
|
+
),
|
|
132
|
+
HookDefinition(
|
|
133
|
+
name="vulture",
|
|
134
|
+
command=[
|
|
135
|
+
"pre-commit",
|
|
136
|
+
"run",
|
|
137
|
+
"--hook-stage",
|
|
138
|
+
"manual",
|
|
139
|
+
"vulture",
|
|
140
|
+
"--all-files",
|
|
141
|
+
],
|
|
142
|
+
timeout=120,
|
|
143
|
+
stage=HookStage.COMPREHENSIVE,
|
|
144
|
+
manual_stage=True,
|
|
145
|
+
),
|
|
146
|
+
HookDefinition(
|
|
147
|
+
name="refurb",
|
|
148
|
+
command=[
|
|
149
|
+
"pre-commit",
|
|
150
|
+
"run",
|
|
151
|
+
"--hook-stage",
|
|
152
|
+
"manual",
|
|
153
|
+
"refurb",
|
|
154
|
+
"--all-files",
|
|
155
|
+
],
|
|
156
|
+
timeout=120,
|
|
157
|
+
stage=HookStage.COMPREHENSIVE,
|
|
158
|
+
manual_stage=True,
|
|
159
|
+
),
|
|
160
|
+
HookDefinition(
|
|
161
|
+
name="creosote",
|
|
162
|
+
command=[
|
|
163
|
+
"pre-commit",
|
|
164
|
+
"run",
|
|
165
|
+
"--hook-stage",
|
|
166
|
+
"manual",
|
|
167
|
+
"creosote",
|
|
168
|
+
"--all-files",
|
|
169
|
+
],
|
|
170
|
+
timeout=120,
|
|
171
|
+
stage=HookStage.COMPREHENSIVE,
|
|
172
|
+
manual_stage=True,
|
|
173
|
+
),
|
|
174
|
+
HookDefinition(
|
|
175
|
+
name="complexipy",
|
|
176
|
+
command=[
|
|
177
|
+
"pre-commit",
|
|
178
|
+
"run",
|
|
179
|
+
"--hook-stage",
|
|
180
|
+
"manual",
|
|
181
|
+
"complexipy",
|
|
182
|
+
"--all-files",
|
|
183
|
+
],
|
|
184
|
+
timeout=120,
|
|
185
|
+
stage=HookStage.COMPREHENSIVE,
|
|
186
|
+
manual_stage=True,
|
|
187
|
+
),
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
FAST_STRATEGY = HookStrategy(
|
|
192
|
+
name="fast",
|
|
193
|
+
hooks=FAST_HOOKS,
|
|
194
|
+
timeout=60,
|
|
195
|
+
retry_policy=RetryPolicy.FORMATTING_ONLY,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
COMPREHENSIVE_STRATEGY = HookStrategy(
|
|
199
|
+
name="comprehensive",
|
|
200
|
+
hooks=COMPREHENSIVE_HOOKS,
|
|
201
|
+
timeout=120,
|
|
202
|
+
retry_policy=RetryPolicy.NONE,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class HookConfigLoader:
|
|
207
|
+
@staticmethod
|
|
208
|
+
def load_strategy(name: str, _: Path | None = None) -> HookStrategy:
|
|
209
|
+
if name == "fast":
|
|
210
|
+
return FAST_STRATEGY
|
|
211
|
+
if name == "comprehensive":
|
|
212
|
+
return COMPREHENSIVE_STRATEGY
|
|
213
|
+
msg = f"Unknown hook strategy: {name}"
|
|
214
|
+
raise ValueError(msg)
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def get_all_strategies() -> dict[str, HookStrategy]:
|
|
218
|
+
return {"fast": FAST_STRATEGY, "comprehensive": COMPREHENSIVE_STRATEGY}
|
|
File without changes
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import time
|
|
4
|
+
import typing as t
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from crackerjack.models.protocols import OptionsProtocol
|
|
10
|
+
|
|
11
|
+
from .phase_coordinator import PhaseCoordinator
|
|
12
|
+
from .session_coordinator import SessionCoordinator
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AsyncWorkflowPipeline:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
console: Console,
|
|
19
|
+
pkg_path: Path,
|
|
20
|
+
session: SessionCoordinator,
|
|
21
|
+
phases: PhaseCoordinator,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.console = console
|
|
24
|
+
self.pkg_path = pkg_path
|
|
25
|
+
self.session = session
|
|
26
|
+
self.phases = phases
|
|
27
|
+
self.logger = logging.getLogger("crackerjack.async_pipeline")
|
|
28
|
+
|
|
29
|
+
async def run_complete_workflow_async(self, options: OptionsProtocol) -> bool:
|
|
30
|
+
start_time = time.time()
|
|
31
|
+
self.session.initialize_session_tracking(options)
|
|
32
|
+
self.session.track_task("workflow", "Complete async crackerjack workflow")
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
if hasattr(options, "ai_agent") and options.ai_agent:
|
|
36
|
+
success = await self._execute_ai_agent_workflow_async(options)
|
|
37
|
+
else:
|
|
38
|
+
success = await self._execute_workflow_phases_async(options)
|
|
39
|
+
self.session.finalize_session(start_time, success)
|
|
40
|
+
return success
|
|
41
|
+
except KeyboardInterrupt:
|
|
42
|
+
self.console.print("Interrupted by user")
|
|
43
|
+
self.session.fail_task("workflow", "Interrupted by user")
|
|
44
|
+
return False
|
|
45
|
+
except Exception as e:
|
|
46
|
+
self.console.print(f"Error: {e}")
|
|
47
|
+
self.session.fail_task("workflow", f"Unexpected error: {e}")
|
|
48
|
+
return False
|
|
49
|
+
finally:
|
|
50
|
+
self.session.cleanup_resources()
|
|
51
|
+
|
|
52
|
+
async def _execute_workflow_phases_async(self, options: OptionsProtocol) -> bool:
|
|
53
|
+
success = True
|
|
54
|
+
|
|
55
|
+
self.phases.run_configuration_phase(options)
|
|
56
|
+
|
|
57
|
+
if not await self._execute_cleaning_phase_async(options):
|
|
58
|
+
success = False
|
|
59
|
+
|
|
60
|
+
self.session.fail_task("workflow", "Cleaning phase failed")
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
if not await self._execute_quality_phase_async(options):
|
|
64
|
+
success = False
|
|
65
|
+
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
if not self.phases.run_publishing_phase(options):
|
|
69
|
+
success = False
|
|
70
|
+
self.session.fail_task("workflow", "Publishing failed")
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
if not self.phases.run_commit_phase(options):
|
|
74
|
+
success = False
|
|
75
|
+
|
|
76
|
+
return success
|
|
77
|
+
|
|
78
|
+
async def _execute_cleaning_phase_async(self, options: OptionsProtocol) -> bool:
|
|
79
|
+
if not options.clean:
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
return await asyncio.to_thread(self.phases.run_cleaning_phase, options)
|
|
83
|
+
|
|
84
|
+
async def _execute_quality_phase_async(self, options: OptionsProtocol) -> bool:
|
|
85
|
+
if hasattr(options, "fast") and options.fast:
|
|
86
|
+
return await self._run_fast_hooks_async(options)
|
|
87
|
+
if hasattr(options, "comp") and options.comp:
|
|
88
|
+
return await self._run_comprehensive_hooks_async(options)
|
|
89
|
+
if options.test:
|
|
90
|
+
return await self._execute_test_workflow_async(options)
|
|
91
|
+
return await self._execute_standard_hooks_workflow_async(options)
|
|
92
|
+
|
|
93
|
+
async def _execute_test_workflow_async(self, options: OptionsProtocol) -> bool:
|
|
94
|
+
overall_success = True
|
|
95
|
+
|
|
96
|
+
if not await self._run_fast_hooks_async(options):
|
|
97
|
+
overall_success = False
|
|
98
|
+
|
|
99
|
+
self.session.fail_task("workflow", "Fast hooks failed")
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
test_task = asyncio.create_task(self._run_testing_phase_async(options))
|
|
103
|
+
hooks_task = asyncio.create_task(self._run_comprehensive_hooks_async(options))
|
|
104
|
+
|
|
105
|
+
test_success, hooks_success = await asyncio.gather(
|
|
106
|
+
test_task,
|
|
107
|
+
hooks_task,
|
|
108
|
+
return_exceptions=True,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if isinstance(test_success, Exception):
|
|
112
|
+
self.logger.error(f"Test execution error: {test_success}")
|
|
113
|
+
test_success = False
|
|
114
|
+
|
|
115
|
+
if isinstance(hooks_success, Exception):
|
|
116
|
+
self.logger.error(f"Hooks execution error: {hooks_success}")
|
|
117
|
+
hooks_success = False
|
|
118
|
+
|
|
119
|
+
if not test_success:
|
|
120
|
+
overall_success = False
|
|
121
|
+
|
|
122
|
+
self.session.fail_task("workflow", "Testing failed")
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
if not hooks_success:
|
|
126
|
+
overall_success = False
|
|
127
|
+
|
|
128
|
+
self.session.fail_task("workflow", "Comprehensive hooks failed")
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
return overall_success
|
|
132
|
+
|
|
133
|
+
async def _execute_standard_hooks_workflow_async(
|
|
134
|
+
self,
|
|
135
|
+
options: OptionsProtocol,
|
|
136
|
+
) -> bool:
|
|
137
|
+
hooks_success = await self._run_hooks_phase_async(options)
|
|
138
|
+
if not hooks_success:
|
|
139
|
+
self.session.fail_task("workflow", "Hooks failed")
|
|
140
|
+
return False
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
async def _run_fast_hooks_async(self, options: OptionsProtocol) -> bool:
|
|
144
|
+
return await asyncio.to_thread(self.phases.run_fast_hooks_only, options)
|
|
145
|
+
|
|
146
|
+
async def _run_comprehensive_hooks_async(self, options: OptionsProtocol) -> bool:
|
|
147
|
+
return await asyncio.to_thread(
|
|
148
|
+
self.phases.run_comprehensive_hooks_only,
|
|
149
|
+
options,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
async def _run_hooks_phase_async(self, options: OptionsProtocol) -> bool:
|
|
153
|
+
return await asyncio.to_thread(self.phases.run_hooks_phase, options)
|
|
154
|
+
|
|
155
|
+
async def _run_testing_phase_async(self, options: OptionsProtocol) -> bool:
|
|
156
|
+
return await asyncio.to_thread(self.phases.run_testing_phase, options)
|
|
157
|
+
|
|
158
|
+
async def _execute_ai_agent_workflow_async(
|
|
159
|
+
self, options: OptionsProtocol, max_iterations: int = 10
|
|
160
|
+
) -> bool:
|
|
161
|
+
"""Execute AI agent workflow with iterative fixing between iterations."""
|
|
162
|
+
self.console.print(
|
|
163
|
+
f"🤖 Starting AI Agent workflow (max {max_iterations} iterations)"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Always run configuration phase first
|
|
167
|
+
self.phases.run_configuration_phase(options)
|
|
168
|
+
|
|
169
|
+
# Run cleaning phase if requested
|
|
170
|
+
if not await self._execute_cleaning_phase_async(options):
|
|
171
|
+
self.session.fail_task("workflow", "Cleaning phase failed")
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
# Iterative quality improvement with AI fixing
|
|
175
|
+
iteration_success = await self._run_iterative_quality_improvement(
|
|
176
|
+
options, max_iterations
|
|
177
|
+
)
|
|
178
|
+
if not iteration_success:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
# Run remaining phases
|
|
182
|
+
return await self._run_final_workflow_phases(options)
|
|
183
|
+
|
|
184
|
+
async def _run_iterative_quality_improvement(
|
|
185
|
+
self, options: OptionsProtocol, max_iterations: int
|
|
186
|
+
) -> bool:
|
|
187
|
+
"""Run iterative quality improvement until all checks pass."""
|
|
188
|
+
for iteration in range(1, max_iterations + 1):
|
|
189
|
+
self.console.print(f"\n🔄 Iteration {iteration}/{max_iterations}")
|
|
190
|
+
|
|
191
|
+
iteration_result = await self._execute_single_iteration(options, iteration)
|
|
192
|
+
|
|
193
|
+
if iteration_result == "success":
|
|
194
|
+
self.console.print("✅ All quality checks passed!")
|
|
195
|
+
return True
|
|
196
|
+
elif iteration_result == "failed":
|
|
197
|
+
return False
|
|
198
|
+
# Continue to next iteration if result == "continue"
|
|
199
|
+
|
|
200
|
+
# If we exhausted all iterations without success
|
|
201
|
+
self.console.print(
|
|
202
|
+
f"❌ Failed to achieve code quality after {max_iterations} iterations"
|
|
203
|
+
)
|
|
204
|
+
self.session.fail_task("workflow", f"Failed after {max_iterations} iterations")
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
async def _execute_single_iteration(
|
|
208
|
+
self, options: OptionsProtocol, iteration: int
|
|
209
|
+
) -> str:
|
|
210
|
+
"""Execute a single AI agent iteration. Returns 'success', 'failed', or 'continue'."""
|
|
211
|
+
# Step 1: Fast hooks with retry logic
|
|
212
|
+
fast_hooks_success = await self._run_fast_hooks_with_retry_async(options)
|
|
213
|
+
|
|
214
|
+
# Step 2 & 3: Collect ALL issues
|
|
215
|
+
test_issues = await self._collect_test_issues_async(options)
|
|
216
|
+
hook_issues = await self._collect_comprehensive_hook_issues_async(options)
|
|
217
|
+
|
|
218
|
+
# If everything passes, we're done
|
|
219
|
+
if fast_hooks_success and not test_issues and not hook_issues:
|
|
220
|
+
return "success"
|
|
221
|
+
|
|
222
|
+
# Step 4: Apply AI fixes for ALL collected issues
|
|
223
|
+
if test_issues or hook_issues:
|
|
224
|
+
fix_success = await self._apply_ai_fixes_async(
|
|
225
|
+
options, test_issues, hook_issues, iteration
|
|
226
|
+
)
|
|
227
|
+
if not fix_success:
|
|
228
|
+
self.console.print(f"❌ AI fixing failed in iteration {iteration}")
|
|
229
|
+
self.session.fail_task(
|
|
230
|
+
"workflow", f"AI fixing failed in iteration {iteration}"
|
|
231
|
+
)
|
|
232
|
+
return "failed"
|
|
233
|
+
|
|
234
|
+
return "continue"
|
|
235
|
+
|
|
236
|
+
async def _run_final_workflow_phases(self, options: OptionsProtocol) -> bool:
|
|
237
|
+
"""Run the final publishing and commit phases."""
|
|
238
|
+
if not self.phases.run_publishing_phase(options):
|
|
239
|
+
self.session.fail_task("workflow", "Publishing failed")
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
if not self.phases.run_commit_phase(options):
|
|
243
|
+
self.session.fail_task("workflow", "Commit failed")
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
return True
|
|
247
|
+
|
|
248
|
+
async def _run_fast_hooks_with_retry_async(self, options: OptionsProtocol) -> bool:
|
|
249
|
+
"""Run fast hooks with one retry if they fail."""
|
|
250
|
+
success = await self._run_fast_hooks_async(options)
|
|
251
|
+
if not success:
|
|
252
|
+
self.console.print("⚠️ Fast hooks failed, retrying once...")
|
|
253
|
+
success = await self._run_fast_hooks_async(options)
|
|
254
|
+
return success
|
|
255
|
+
|
|
256
|
+
async def _collect_test_issues_async(self, options: OptionsProtocol) -> list[str]:
|
|
257
|
+
"""Collect all test failures without stopping on first failure."""
|
|
258
|
+
if not options.test:
|
|
259
|
+
return []
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
success = await self._run_testing_phase_async(options)
|
|
263
|
+
if success:
|
|
264
|
+
return []
|
|
265
|
+
else:
|
|
266
|
+
return ["Test failures detected - see logs for details"]
|
|
267
|
+
except Exception as e:
|
|
268
|
+
return [f"Test execution error: {e}"]
|
|
269
|
+
|
|
270
|
+
async def _collect_comprehensive_hook_issues_async(
|
|
271
|
+
self, options: OptionsProtocol
|
|
272
|
+
) -> list[str]:
|
|
273
|
+
"""Collect all comprehensive hook issues without stopping on first failure."""
|
|
274
|
+
try:
|
|
275
|
+
success = await self._run_comprehensive_hooks_async(options)
|
|
276
|
+
if success:
|
|
277
|
+
return []
|
|
278
|
+
else:
|
|
279
|
+
return ["Comprehensive hook failures detected - see logs for details"]
|
|
280
|
+
except Exception as e:
|
|
281
|
+
return [f"Comprehensive hooks error: {e}"]
|
|
282
|
+
|
|
283
|
+
async def _apply_ai_fixes_async(
|
|
284
|
+
self,
|
|
285
|
+
options: OptionsProtocol,
|
|
286
|
+
test_issues: list[str],
|
|
287
|
+
hook_issues: list[str],
|
|
288
|
+
iteration: int,
|
|
289
|
+
) -> bool:
|
|
290
|
+
"""Apply AI fixes for all collected issues in batch."""
|
|
291
|
+
all_issues = test_issues + hook_issues
|
|
292
|
+
if not all_issues:
|
|
293
|
+
return True
|
|
294
|
+
|
|
295
|
+
self.console.print(
|
|
296
|
+
f"🔧 Applying AI fixes for {len(all_issues)} issues in iteration {iteration}"
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# This would integrate with the AI agent system to actually apply fixes
|
|
300
|
+
# For now, we'll simulate the fixing process
|
|
301
|
+
try:
|
|
302
|
+
# In a real implementation, this would:
|
|
303
|
+
# 1. Analyze all collected issues
|
|
304
|
+
# 2. Generate fixes using AI agents
|
|
305
|
+
# 3. Apply the fixes to source code
|
|
306
|
+
# 4. Return success/failure status
|
|
307
|
+
|
|
308
|
+
# Placeholder for actual AI fixing logic
|
|
309
|
+
await asyncio.sleep(0.1) # Simulate processing time
|
|
310
|
+
|
|
311
|
+
self.console.print(f"✅ AI fixes applied for iteration {iteration}")
|
|
312
|
+
return True
|
|
313
|
+
|
|
314
|
+
except Exception as e:
|
|
315
|
+
self.logger.error(f"AI fixing failed: {e}")
|
|
316
|
+
return False
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class AsyncWorkflowOrchestrator:
|
|
320
|
+
def __init__(
|
|
321
|
+
self,
|
|
322
|
+
console: Console | None = None,
|
|
323
|
+
pkg_path: Path | None = None,
|
|
324
|
+
dry_run: bool = False,
|
|
325
|
+
web_job_id: str | None = None,
|
|
326
|
+
) -> None:
|
|
327
|
+
self.console = console or Console(force_terminal=True)
|
|
328
|
+
self.pkg_path = pkg_path or Path.cwd()
|
|
329
|
+
self.dry_run = dry_run
|
|
330
|
+
self.web_job_id = web_job_id
|
|
331
|
+
|
|
332
|
+
from crackerjack.models.protocols import (
|
|
333
|
+
FileSystemInterface,
|
|
334
|
+
GitInterface,
|
|
335
|
+
HookManager,
|
|
336
|
+
PublishManager,
|
|
337
|
+
TestManagerProtocol,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
from .container import create_container
|
|
341
|
+
|
|
342
|
+
self.container = create_container(
|
|
343
|
+
console=self.console,
|
|
344
|
+
pkg_path=self.pkg_path,
|
|
345
|
+
dry_run=self.dry_run,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
self.session = SessionCoordinator(self.console, self.pkg_path, self.web_job_id)
|
|
349
|
+
self.phases = PhaseCoordinator(
|
|
350
|
+
console=self.console,
|
|
351
|
+
pkg_path=self.pkg_path,
|
|
352
|
+
session=self.session,
|
|
353
|
+
filesystem=self.container.get(FileSystemInterface),
|
|
354
|
+
git_service=self.container.get(GitInterface),
|
|
355
|
+
hook_manager=self.container.get(HookManager),
|
|
356
|
+
test_manager=self.container.get(TestManagerProtocol),
|
|
357
|
+
publish_manager=self.container.get(PublishManager),
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
self.async_pipeline = AsyncWorkflowPipeline(
|
|
361
|
+
console=self.console,
|
|
362
|
+
pkg_path=self.pkg_path,
|
|
363
|
+
session=self.session,
|
|
364
|
+
phases=self.phases,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
self.logger = logging.getLogger("crackerjack.async_orchestrator")
|
|
368
|
+
|
|
369
|
+
async def run_complete_workflow_async(self, options: OptionsProtocol) -> bool:
|
|
370
|
+
return await self.async_pipeline.run_complete_workflow_async(options)
|
|
371
|
+
|
|
372
|
+
def run_complete_workflow(self, options: OptionsProtocol) -> bool:
|
|
373
|
+
return asyncio.run(self.run_complete_workflow_async(options))
|
|
374
|
+
|
|
375
|
+
def run_cleaning_phase(self, options: OptionsProtocol) -> bool:
|
|
376
|
+
return self.phases.run_cleaning_phase(options)
|
|
377
|
+
|
|
378
|
+
def run_fast_hooks_only(self, options: OptionsProtocol) -> bool:
|
|
379
|
+
return self.phases.run_fast_hooks_only(options)
|
|
380
|
+
|
|
381
|
+
def run_comprehensive_hooks_only(self, options: OptionsProtocol) -> bool:
|
|
382
|
+
return self.phases.run_comprehensive_hooks_only(options)
|
|
383
|
+
|
|
384
|
+
def run_hooks_phase(self, options: OptionsProtocol) -> bool:
|
|
385
|
+
return self.phases.run_hooks_phase(options)
|
|
386
|
+
|
|
387
|
+
def run_testing_phase(self, options: OptionsProtocol) -> bool:
|
|
388
|
+
return self.phases.run_testing_phase(options)
|
|
389
|
+
|
|
390
|
+
def run_publishing_phase(self, options: OptionsProtocol) -> bool:
|
|
391
|
+
return self.phases.run_publishing_phase(options)
|
|
392
|
+
|
|
393
|
+
def run_commit_phase(self, options: OptionsProtocol) -> bool:
|
|
394
|
+
return self.phases.run_commit_phase(options)
|
|
395
|
+
|
|
396
|
+
def run_configuration_phase(self, options: OptionsProtocol) -> bool:
|
|
397
|
+
return self.phases.run_configuration_phase(options)
|
|
398
|
+
|
|
399
|
+
def _cleanup_resources(self) -> None:
|
|
400
|
+
self.session.cleanup_resources()
|
|
401
|
+
|
|
402
|
+
def _register_cleanup(self, cleanup_handler: t.Callable[[], None]) -> None:
|
|
403
|
+
self.session.register_cleanup(cleanup_handler)
|
|
404
|
+
|
|
405
|
+
def _track_lock_file(self, lock_file_path: Path) -> None:
|
|
406
|
+
self.session.track_lock_file(lock_file_path)
|