crackerjack 0.30.3__py3-none-any.whl → 0.31.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (155) hide show
  1. crackerjack/CLAUDE.md +1005 -0
  2. crackerjack/RULES.md +380 -0
  3. crackerjack/__init__.py +42 -13
  4. crackerjack/__main__.py +225 -299
  5. crackerjack/agents/__init__.py +41 -0
  6. crackerjack/agents/architect_agent.py +281 -0
  7. crackerjack/agents/base.py +169 -0
  8. crackerjack/agents/coordinator.py +512 -0
  9. crackerjack/agents/documentation_agent.py +498 -0
  10. crackerjack/agents/dry_agent.py +388 -0
  11. crackerjack/agents/formatting_agent.py +245 -0
  12. crackerjack/agents/import_optimization_agent.py +281 -0
  13. crackerjack/agents/performance_agent.py +669 -0
  14. crackerjack/agents/proactive_agent.py +104 -0
  15. crackerjack/agents/refactoring_agent.py +788 -0
  16. crackerjack/agents/security_agent.py +529 -0
  17. crackerjack/agents/test_creation_agent.py +652 -0
  18. crackerjack/agents/test_specialist_agent.py +486 -0
  19. crackerjack/agents/tracker.py +212 -0
  20. crackerjack/api.py +560 -0
  21. crackerjack/cli/__init__.py +24 -0
  22. crackerjack/cli/facade.py +104 -0
  23. crackerjack/cli/handlers.py +267 -0
  24. crackerjack/cli/interactive.py +471 -0
  25. crackerjack/cli/options.py +401 -0
  26. crackerjack/cli/utils.py +18 -0
  27. crackerjack/code_cleaner.py +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 +561 -0
  37. crackerjack/core/proactive_workflow.py +316 -0
  38. crackerjack/core/session_coordinator.py +289 -0
  39. crackerjack/core/workflow_orchestrator.py +640 -0
  40. crackerjack/dynamic_config.py +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 +411 -0
  58. crackerjack/managers/test_command_builder.py +151 -0
  59. crackerjack/managers/test_executor.py +435 -0
  60. crackerjack/managers/test_manager.py +258 -0
  61. crackerjack/managers/test_manager_backup.py +1124 -0
  62. crackerjack/managers/test_progress.py +144 -0
  63. crackerjack/mcp/__init__.py +0 -0
  64. crackerjack/mcp/cache.py +336 -0
  65. crackerjack/mcp/client_runner.py +104 -0
  66. crackerjack/mcp/context.py +615 -0
  67. crackerjack/mcp/dashboard.py +636 -0
  68. crackerjack/mcp/enhanced_progress_monitor.py +479 -0
  69. crackerjack/mcp/file_monitor.py +336 -0
  70. crackerjack/mcp/progress_components.py +569 -0
  71. crackerjack/mcp/progress_monitor.py +949 -0
  72. crackerjack/mcp/rate_limiter.py +332 -0
  73. crackerjack/mcp/server.py +22 -0
  74. crackerjack/mcp/server_core.py +244 -0
  75. crackerjack/mcp/service_watchdog.py +501 -0
  76. crackerjack/mcp/state.py +395 -0
  77. crackerjack/mcp/task_manager.py +257 -0
  78. crackerjack/mcp/tools/__init__.py +17 -0
  79. crackerjack/mcp/tools/core_tools.py +249 -0
  80. crackerjack/mcp/tools/error_analyzer.py +308 -0
  81. crackerjack/mcp/tools/execution_tools.py +370 -0
  82. crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
  83. crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
  84. crackerjack/mcp/tools/intelligence_tools.py +314 -0
  85. crackerjack/mcp/tools/monitoring_tools.py +502 -0
  86. crackerjack/mcp/tools/proactive_tools.py +384 -0
  87. crackerjack/mcp/tools/progress_tools.py +141 -0
  88. crackerjack/mcp/tools/utility_tools.py +341 -0
  89. crackerjack/mcp/tools/workflow_executor.py +360 -0
  90. crackerjack/mcp/websocket/__init__.py +14 -0
  91. crackerjack/mcp/websocket/app.py +39 -0
  92. crackerjack/mcp/websocket/endpoints.py +559 -0
  93. crackerjack/mcp/websocket/jobs.py +253 -0
  94. crackerjack/mcp/websocket/server.py +116 -0
  95. crackerjack/mcp/websocket/websocket_handler.py +78 -0
  96. crackerjack/mcp/websocket_server.py +10 -0
  97. crackerjack/models/__init__.py +31 -0
  98. crackerjack/models/config.py +93 -0
  99. crackerjack/models/config_adapter.py +230 -0
  100. crackerjack/models/protocols.py +118 -0
  101. crackerjack/models/task.py +154 -0
  102. crackerjack/monitoring/ai_agent_watchdog.py +450 -0
  103. crackerjack/monitoring/regression_prevention.py +638 -0
  104. crackerjack/orchestration/__init__.py +0 -0
  105. crackerjack/orchestration/advanced_orchestrator.py +970 -0
  106. crackerjack/orchestration/execution_strategies.py +341 -0
  107. crackerjack/orchestration/test_progress_streamer.py +636 -0
  108. crackerjack/plugins/__init__.py +15 -0
  109. crackerjack/plugins/base.py +200 -0
  110. crackerjack/plugins/hooks.py +246 -0
  111. crackerjack/plugins/loader.py +335 -0
  112. crackerjack/plugins/managers.py +259 -0
  113. crackerjack/py313.py +8 -3
  114. crackerjack/services/__init__.py +22 -0
  115. crackerjack/services/cache.py +314 -0
  116. crackerjack/services/config.py +347 -0
  117. crackerjack/services/config_integrity.py +99 -0
  118. crackerjack/services/contextual_ai_assistant.py +516 -0
  119. crackerjack/services/coverage_ratchet.py +347 -0
  120. crackerjack/services/debug.py +736 -0
  121. crackerjack/services/dependency_monitor.py +617 -0
  122. crackerjack/services/enhanced_filesystem.py +439 -0
  123. crackerjack/services/file_hasher.py +151 -0
  124. crackerjack/services/filesystem.py +395 -0
  125. crackerjack/services/git.py +165 -0
  126. crackerjack/services/health_metrics.py +611 -0
  127. crackerjack/services/initialization.py +847 -0
  128. crackerjack/services/log_manager.py +286 -0
  129. crackerjack/services/logging.py +174 -0
  130. crackerjack/services/metrics.py +578 -0
  131. crackerjack/services/pattern_cache.py +362 -0
  132. crackerjack/services/pattern_detector.py +515 -0
  133. crackerjack/services/performance_benchmarks.py +653 -0
  134. crackerjack/services/security.py +163 -0
  135. crackerjack/services/server_manager.py +234 -0
  136. crackerjack/services/smart_scheduling.py +144 -0
  137. crackerjack/services/tool_version_service.py +61 -0
  138. crackerjack/services/unified_config.py +437 -0
  139. crackerjack/services/version_checker.py +248 -0
  140. crackerjack/slash_commands/__init__.py +14 -0
  141. crackerjack/slash_commands/init.md +122 -0
  142. crackerjack/slash_commands/run.md +163 -0
  143. crackerjack/slash_commands/status.md +127 -0
  144. crackerjack-0.31.4.dist-info/METADATA +742 -0
  145. crackerjack-0.31.4.dist-info/RECORD +148 -0
  146. crackerjack-0.31.4.dist-info/entry_points.txt +2 -0
  147. crackerjack/.gitignore +0 -34
  148. crackerjack/.libcst.codemod.yaml +0 -18
  149. crackerjack/.pdm.toml +0 -1
  150. crackerjack/crackerjack.py +0 -3805
  151. crackerjack/pyproject.toml +0 -286
  152. crackerjack-0.30.3.dist-info/METADATA +0 -1290
  153. crackerjack-0.30.3.dist-info/RECORD +0 -16
  154. {crackerjack-0.30.3.dist-info → crackerjack-0.31.4.dist-info}/WHEEL +0 -0
  155. {crackerjack-0.30.3.dist-info → crackerjack-0.31.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,19 @@
1
+ from .hooks import (
2
+ COMPREHENSIVE_STRATEGY,
3
+ FAST_STRATEGY,
4
+ HookConfigLoader,
5
+ HookDefinition,
6
+ HookStage,
7
+ HookStrategy,
8
+ RetryPolicy,
9
+ )
10
+
11
+ __all__ = [
12
+ "COMPREHENSIVE_STRATEGY",
13
+ "FAST_STRATEGY",
14
+ "HookConfigLoader",
15
+ "HookDefinition",
16
+ "HookStage",
17
+ "HookStrategy",
18
+ "RetryPolicy",
19
+ ]
@@ -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)