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,551 @@
1
+ """Multi-Agent Execution Orchestrator.
2
+
3
+ Coordinates execution of multiple agents with smart routing, fallback strategies,
4
+ and result aggregation.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import typing as t
10
+ from dataclasses import dataclass
11
+ from enum import Enum
12
+
13
+ from crackerjack.agents.base import AgentContext, Issue
14
+
15
+ from .agent_registry import (
16
+ AgentRegistry,
17
+ AgentSource,
18
+ RegisteredAgent,
19
+ get_agent_registry,
20
+ )
21
+ from .agent_selector import AgentScore, AgentSelector, TaskDescription
22
+
23
+
24
+ class ExecutionStrategy(Enum):
25
+ """Strategy for multi-agent execution."""
26
+
27
+ SINGLE_BEST = "single_best" # Use only the highest-scored agent
28
+ PARALLEL = "parallel" # Run multiple agents in parallel
29
+ SEQUENTIAL = "sequential" # Run agents one by one until success
30
+ CONSENSUS = "consensus" # Run multiple agents and compare results
31
+
32
+
33
+ class ExecutionMode(Enum):
34
+ """Mode of execution."""
35
+
36
+ AUTONOMOUS = "autonomous" # Full automation
37
+ GUIDED = "guided" # With human oversight
38
+ ADVISORY = "advisory" # Recommendations only
39
+
40
+
41
+ @dataclass
42
+ class ExecutionRequest:
43
+ """Request for agent execution."""
44
+
45
+ task: TaskDescription
46
+ strategy: ExecutionStrategy = ExecutionStrategy.SINGLE_BEST
47
+ mode: ExecutionMode = ExecutionMode.AUTONOMOUS
48
+ max_agents: int = 3
49
+ timeout_seconds: int = 300
50
+ fallback_to_system: bool = True
51
+ context: AgentContext | None = None
52
+
53
+
54
+ @dataclass
55
+ class ExecutionResult:
56
+ """Result of agent execution."""
57
+
58
+ success: bool
59
+ primary_result: t.Any | None # Main result from best agent
60
+ all_results: list[tuple[RegisteredAgent, t.Any]] # All agent results
61
+ execution_time: float
62
+ agents_used: list[str]
63
+ strategy_used: ExecutionStrategy
64
+ error_message: str | None = None
65
+ recommendations: list[str] | None = None
66
+
67
+
68
+ class AgentOrchestrator:
69
+ """Multi-agent execution orchestrator."""
70
+
71
+ def __init__(
72
+ self,
73
+ registry: AgentRegistry | None = None,
74
+ selector: AgentSelector | None = None,
75
+ ) -> None:
76
+ self.logger = logging.getLogger(__name__)
77
+ self.registry = registry
78
+ self.selector = selector or AgentSelector(registry)
79
+ self._execution_stats: dict[str, int] = {}
80
+
81
+ async def execute(self, request: ExecutionRequest) -> ExecutionResult:
82
+ """Execute a request using the intelligent agent system."""
83
+ start_time = asyncio.get_event_loop().time()
84
+
85
+ try:
86
+ self.logger.info(
87
+ f"Executing request: {request.task.description[:50]}... "
88
+ f"(strategy: {request.strategy.value})"
89
+ )
90
+
91
+ # Get agent candidates
92
+ candidates = await self.selector.select_agents(
93
+ request.task, max_candidates=request.max_agents
94
+ )
95
+
96
+ if not candidates:
97
+ return self._create_error_result(
98
+ "No suitable agents found for task",
99
+ start_time,
100
+ request.strategy,
101
+ )
102
+
103
+ # Execute based on strategy
104
+ if request.strategy == ExecutionStrategy.SINGLE_BEST:
105
+ result = await self._execute_single_best(request, candidates)
106
+ elif request.strategy == ExecutionStrategy.PARALLEL:
107
+ result = await self._execute_parallel(request, candidates)
108
+ elif request.strategy == ExecutionStrategy.SEQUENTIAL:
109
+ result = await self._execute_sequential(request, candidates)
110
+ elif request.strategy == ExecutionStrategy.CONSENSUS:
111
+ result = await self._execute_consensus(request, candidates)
112
+ else:
113
+ result = await self._execute_single_best(request, candidates)
114
+
115
+ execution_time = asyncio.get_event_loop().time() - start_time
116
+ result.execution_time = execution_time
117
+
118
+ # Update stats
119
+ for agent_name in result.agents_used:
120
+ self._execution_stats[agent_name] = (
121
+ self._execution_stats.get(agent_name, 0) + 1
122
+ )
123
+
124
+ self.logger.info(
125
+ f"Execution completed in {execution_time:.2f}s: "
126
+ f"{'success' if result.success else 'failure'} "
127
+ f"using {len(result.agents_used)} agents"
128
+ )
129
+
130
+ return result
131
+
132
+ except Exception as e:
133
+ self.logger.exception(f"Execution failed: {e}")
134
+ return self._create_error_result(
135
+ f"Execution error: {e}",
136
+ start_time,
137
+ request.strategy,
138
+ )
139
+
140
+ async def _execute_single_best(
141
+ self,
142
+ request: ExecutionRequest,
143
+ candidates: list[AgentScore],
144
+ ) -> ExecutionResult:
145
+ """Execute using the single best agent."""
146
+ best_candidate = candidates[0]
147
+
148
+ try:
149
+ result = await self._execute_agent(best_candidate.agent, request)
150
+
151
+ return ExecutionResult(
152
+ success=True,
153
+ primary_result=result,
154
+ all_results=[(best_candidate.agent, result)],
155
+ execution_time=0.0, # Will be set by caller
156
+ agents_used=[best_candidate.agent.metadata.name],
157
+ strategy_used=ExecutionStrategy.SINGLE_BEST,
158
+ recommendations=self._generate_recommendations(best_candidate),
159
+ )
160
+
161
+ except Exception as e:
162
+ # Fallback to next best agent if available
163
+ if len(candidates) > 1 and request.fallback_to_system:
164
+ self.logger.warning(
165
+ f"Primary agent {best_candidate.agent.metadata.name} failed: {e}. "
166
+ f"Trying fallback..."
167
+ )
168
+
169
+ fallback_request = ExecutionRequest(
170
+ task=request.task,
171
+ strategy=ExecutionStrategy.SEQUENTIAL,
172
+ mode=request.mode,
173
+ max_agents=len(candidates) - 1,
174
+ timeout_seconds=request.timeout_seconds,
175
+ fallback_to_system=False, # Prevent infinite recursion
176
+ context=request.context,
177
+ )
178
+
179
+ return await self._execute_sequential(fallback_request, candidates[1:])
180
+
181
+ return ExecutionResult(
182
+ success=False,
183
+ primary_result=None,
184
+ all_results=[(best_candidate.agent, e)],
185
+ execution_time=0.0,
186
+ agents_used=[],
187
+ strategy_used=ExecutionStrategy.SINGLE_BEST,
188
+ error_message=str(e),
189
+ )
190
+
191
+ async def _execute_parallel(
192
+ self,
193
+ request: ExecutionRequest,
194
+ candidates: list[AgentScore],
195
+ ) -> ExecutionResult:
196
+ """Execute multiple agents in parallel."""
197
+ tasks = []
198
+ agents_to_execute = candidates[: request.max_agents]
199
+
200
+ for candidate in agents_to_execute:
201
+ task = asyncio.create_task(
202
+ self._execute_agent_safe(candidate.agent, request)
203
+ )
204
+ tasks.append((candidate.agent, task))
205
+
206
+ # Wait for all tasks to complete
207
+ results = []
208
+ successful_results = []
209
+
210
+ for agent, task in tasks:
211
+ try:
212
+ result = await asyncio.wait_for(task, timeout=request.timeout_seconds)
213
+ results.append((agent, result))
214
+ if not isinstance(result, Exception):
215
+ successful_results.append((agent, result))
216
+ except TimeoutError:
217
+ results.append((agent, TimeoutError("Agent execution timed out")))
218
+ except Exception as e:
219
+ results.append((agent, e))
220
+
221
+ # Choose best successful result
222
+ primary_result = None
223
+ agents_used = []
224
+
225
+ if successful_results:
226
+ # Use result from highest-priority agent
227
+ successful_results.sort(key=lambda x: x[0].metadata.priority, reverse=True)
228
+ primary_result = successful_results[0][1]
229
+ agents_used = [agent.metadata.name for agent, _ in successful_results]
230
+
231
+ return ExecutionResult(
232
+ success=len(successful_results) > 0,
233
+ primary_result=primary_result,
234
+ all_results=results,
235
+ execution_time=0.0,
236
+ agents_used=agents_used,
237
+ strategy_used=ExecutionStrategy.PARALLEL,
238
+ error_message=None if successful_results else "All parallel agents failed",
239
+ )
240
+
241
+ async def _execute_sequential(
242
+ self,
243
+ request: ExecutionRequest,
244
+ candidates: list[AgentScore],
245
+ ) -> ExecutionResult:
246
+ """Execute agents sequentially until one succeeds."""
247
+ results = []
248
+
249
+ for candidate in candidates[: request.max_agents]:
250
+ try:
251
+ result = await asyncio.wait_for(
252
+ self._execute_agent(candidate.agent, request),
253
+ timeout=request.timeout_seconds,
254
+ )
255
+
256
+ results.append((candidate.agent, result))
257
+
258
+ # Success - return immediately
259
+ return ExecutionResult(
260
+ success=True,
261
+ primary_result=result,
262
+ all_results=results,
263
+ execution_time=0.0,
264
+ agents_used=[candidate.agent.metadata.name],
265
+ strategy_used=ExecutionStrategy.SEQUENTIAL,
266
+ recommendations=self._generate_recommendations(candidate),
267
+ )
268
+
269
+ except Exception as e:
270
+ results.append((candidate.agent, e))
271
+ self.logger.warning(
272
+ f"Sequential agent {candidate.agent.metadata.name} failed: {e}"
273
+ )
274
+ continue
275
+
276
+ # All agents failed
277
+ return ExecutionResult(
278
+ success=False,
279
+ primary_result=None,
280
+ all_results=results,
281
+ execution_time=0.0,
282
+ agents_used=[],
283
+ strategy_used=ExecutionStrategy.SEQUENTIAL,
284
+ error_message="All sequential agents failed",
285
+ )
286
+
287
+ async def _execute_consensus(
288
+ self,
289
+ request: ExecutionRequest,
290
+ candidates: list[AgentScore],
291
+ ) -> ExecutionResult:
292
+ """Execute multiple agents and build consensus from results."""
293
+ # First run parallel execution
294
+ parallel_request = ExecutionRequest(
295
+ task=request.task,
296
+ strategy=ExecutionStrategy.PARALLEL,
297
+ mode=request.mode,
298
+ max_agents=min(request.max_agents, 3), # Limit for consensus
299
+ timeout_seconds=request.timeout_seconds,
300
+ fallback_to_system=False,
301
+ context=request.context,
302
+ )
303
+
304
+ parallel_result = await self._execute_parallel(parallel_request, candidates)
305
+
306
+ if not parallel_result.success:
307
+ return parallel_result
308
+
309
+ # Analyze results for consensus
310
+ successful_results = [
311
+ (agent, result)
312
+ for agent, result in parallel_result.all_results
313
+ if not isinstance(result, Exception)
314
+ ]
315
+
316
+ if len(successful_results) < 2:
317
+ # Not enough results for consensus - return best result
318
+ return parallel_result
319
+
320
+ # Build consensus (simplified - could be much more sophisticated)
321
+ consensus_result = self._build_consensus(successful_results)
322
+
323
+ return ExecutionResult(
324
+ success=True,
325
+ primary_result=consensus_result,
326
+ all_results=parallel_result.all_results,
327
+ execution_time=parallel_result.execution_time,
328
+ agents_used=parallel_result.agents_used,
329
+ strategy_used=ExecutionStrategy.CONSENSUS,
330
+ recommendations=["Results validated through multi-agent consensus"],
331
+ )
332
+
333
+ async def _execute_agent(
334
+ self, agent: RegisteredAgent, request: ExecutionRequest
335
+ ) -> t.Any:
336
+ """Execute a specific agent."""
337
+ if agent.agent is not None:
338
+ # Crackerjack agent
339
+ return await self._execute_crackerjack_agent(agent, request)
340
+ elif agent.agent_path is not None:
341
+ # User agent
342
+ return await self._execute_user_agent(agent, request)
343
+ elif agent.subagent_type is not None:
344
+ # System agent
345
+ return await self._execute_system_agent(agent, request)
346
+ else:
347
+ raise ValueError(f"Invalid agent configuration: {agent.metadata.name}")
348
+
349
+ async def _execute_agent_safe(
350
+ self, agent: RegisteredAgent, request: ExecutionRequest
351
+ ) -> t.Any:
352
+ """Execute an agent with exception handling."""
353
+ try:
354
+ return await self._execute_agent(agent, request)
355
+ except Exception as e:
356
+ return e
357
+
358
+ async def _execute_crackerjack_agent(
359
+ self,
360
+ agent: RegisteredAgent,
361
+ request: ExecutionRequest,
362
+ ) -> t.Any:
363
+ """Execute a built-in crackerjack agent."""
364
+ if not agent.agent:
365
+ raise ValueError("No crackerjack agent instance available")
366
+
367
+ # Convert task to Issue for crackerjack agents
368
+ issue = Issue(
369
+ id="orchestrated_task",
370
+ type=self._map_task_to_issue_type(request.task),
371
+ severity=self._map_task_priority_to_severity(request.task),
372
+ message=request.task.description,
373
+ file_path=None, # Could be extracted from task if needed
374
+ )
375
+
376
+ # Execute agent
377
+ result = await agent.agent.analyze_and_fix(issue)
378
+ return result
379
+
380
+ async def _execute_user_agent(
381
+ self,
382
+ agent: RegisteredAgent,
383
+ request: ExecutionRequest,
384
+ ) -> t.Any:
385
+ """Execute a user agent via Task tool."""
386
+ # Import Task tool dynamically to avoid circular imports
387
+ from crackerjack.mcp.tools.core_tools import create_task_with_subagent
388
+
389
+ # Use Task tool to execute user agent
390
+ result = await create_task_with_subagent(
391
+ description=f"Execute task using {agent.metadata.name}",
392
+ prompt=request.task.description,
393
+ subagent_type=agent.metadata.name,
394
+ )
395
+
396
+ return result
397
+
398
+ async def _execute_system_agent(
399
+ self,
400
+ agent: RegisteredAgent,
401
+ request: ExecutionRequest,
402
+ ) -> t.Any:
403
+ """Execute a system agent via Task tool."""
404
+ if not agent.subagent_type:
405
+ raise ValueError("No subagent type specified for system agent")
406
+
407
+ # Import Task tool dynamically
408
+ from crackerjack.mcp.tools.core_tools import create_task_with_subagent
409
+
410
+ # Use Task tool to execute system agent
411
+ result = await create_task_with_subagent(
412
+ description=f"Execute task using {agent.metadata.name}",
413
+ prompt=request.task.description,
414
+ subagent_type=agent.subagent_type,
415
+ )
416
+
417
+ return result
418
+
419
+ def _map_task_to_issue_type(self, task: TaskDescription):
420
+ """Map task context to Issue type for crackerjack agents."""
421
+ # Import IssueType here to avoid circular imports
422
+ from crackerjack.agents.base import IssueType
423
+
424
+ # Simple mapping - could be more sophisticated
425
+ context_map = {
426
+ "code_quality": IssueType.FORMATTING,
427
+ "refactoring": IssueType.COMPLEXITY,
428
+ "testing": IssueType.TEST_FAILURE,
429
+ "security": IssueType.SECURITY,
430
+ "performance": IssueType.PERFORMANCE,
431
+ "documentation": IssueType.DOCUMENTATION,
432
+ }
433
+
434
+ if task.context and task.context.value in context_map:
435
+ return context_map[task.context.value]
436
+
437
+ # Analyze task description for hints
438
+ desc_lower = task.description.lower()
439
+ if "test" in desc_lower:
440
+ return IssueType.TEST_FAILURE
441
+ elif "refurb" in desc_lower or "complexity" in desc_lower:
442
+ return IssueType.COMPLEXITY
443
+ elif "security" in desc_lower:
444
+ return IssueType.SECURITY
445
+ elif "format" in desc_lower:
446
+ return IssueType.FORMATTING
447
+
448
+ return IssueType.FORMATTING # Default
449
+
450
+ def _map_task_priority_to_severity(self, task: TaskDescription):
451
+ """Map task priority to Issue severity."""
452
+ from crackerjack.agents.base import Priority
453
+
454
+ if task.priority >= 80:
455
+ return Priority.HIGH
456
+ elif task.priority >= 50:
457
+ return Priority.MEDIUM
458
+
459
+ return Priority.LOW
460
+
461
+ def _build_consensus(self, results: list[tuple[RegisteredAgent, t.Any]]) -> t.Any:
462
+ """Build consensus from multiple agent results."""
463
+ # Simplified consensus - could be much more sophisticated
464
+ # For now, just return the result from the highest-priority agent
465
+ results.sort(key=lambda x: x[0].metadata.priority, reverse=True)
466
+ return results[0][1]
467
+
468
+ def _generate_recommendations(self, candidate: AgentScore) -> list[str]:
469
+ """Generate recommendations based on agent selection."""
470
+ recommendations = []
471
+
472
+ if candidate.final_score > 0.8:
473
+ recommendations.append("High confidence in agent selection")
474
+ elif candidate.final_score > 0.6:
475
+ recommendations.append("Good agent match for this task")
476
+ else:
477
+ recommendations.append("Consider manual review of results")
478
+
479
+ if candidate.agent.metadata.source == AgentSource.CRACKERJACK:
480
+ recommendations.append("Using specialized built-in agent")
481
+ elif candidate.agent.metadata.source == AgentSource.USER:
482
+ recommendations.append("Using custom user agent")
483
+ else:
484
+ recommendations.append("Using general-purpose system agent")
485
+
486
+ return recommendations
487
+
488
+ def _create_error_result(
489
+ self,
490
+ error_message: str,
491
+ start_time: float,
492
+ strategy: ExecutionStrategy,
493
+ ) -> ExecutionResult:
494
+ """Create an error result."""
495
+ execution_time = asyncio.get_event_loop().time() - start_time
496
+
497
+ return ExecutionResult(
498
+ success=False,
499
+ primary_result=None,
500
+ all_results=[],
501
+ execution_time=execution_time,
502
+ agents_used=[],
503
+ strategy_used=strategy,
504
+ error_message=error_message,
505
+ )
506
+
507
+ def get_execution_stats(self) -> dict[str, t.Any]:
508
+ """Get execution statistics."""
509
+ from operator import itemgetter
510
+
511
+ return {
512
+ "total_executions": sum(self._execution_stats.values()),
513
+ "agent_usage": self._execution_stats.copy(),
514
+ "most_used_agent": max(
515
+ self._execution_stats.items(), key=itemgetter(1), default=("none", 0)
516
+ )[0]
517
+ if self._execution_stats
518
+ else "none",
519
+ }
520
+
521
+ async def analyze_task_routing(self, task: TaskDescription) -> dict[str, t.Any]:
522
+ """Analyze how a task would be routed through the system."""
523
+ analysis = await self.selector.analyze_task_complexity(task)
524
+
525
+ # Add orchestration recommendations
526
+ if analysis["complexity_level"] == "high":
527
+ analysis["recommended_strategy"] = ExecutionStrategy.CONSENSUS
528
+ elif analysis["candidate_count"] > 3:
529
+ analysis["recommended_strategy"] = ExecutionStrategy.PARALLEL
530
+ elif analysis["candidate_count"] > 1:
531
+ analysis["recommended_strategy"] = ExecutionStrategy.SEQUENTIAL
532
+ else:
533
+ analysis["recommended_strategy"] = ExecutionStrategy.SINGLE_BEST
534
+
535
+ return analysis
536
+
537
+
538
+ # Global orchestrator instance
539
+ _orchestrator_instance: AgentOrchestrator | None = None
540
+
541
+
542
+ async def get_agent_orchestrator() -> AgentOrchestrator:
543
+ """Get or create the global agent orchestrator."""
544
+ global _orchestrator_instance
545
+
546
+ if _orchestrator_instance is None:
547
+ registry = await get_agent_registry()
548
+ selector = AgentSelector(registry)
549
+ _orchestrator_instance = AgentOrchestrator(registry, selector)
550
+
551
+ return _orchestrator_instance