crackerjack 0.31.10__py3-none-any.whl → 0.31.13__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 +288 -705
- crackerjack/__main__.py +22 -8
- crackerjack/agents/__init__.py +0 -3
- crackerjack/agents/architect_agent.py +0 -43
- crackerjack/agents/base.py +1 -9
- crackerjack/agents/coordinator.py +2 -148
- crackerjack/agents/documentation_agent.py +109 -81
- crackerjack/agents/dry_agent.py +122 -97
- crackerjack/agents/formatting_agent.py +3 -16
- crackerjack/agents/import_optimization_agent.py +1174 -130
- crackerjack/agents/performance_agent.py +956 -188
- crackerjack/agents/performance_helpers.py +229 -0
- crackerjack/agents/proactive_agent.py +1 -48
- crackerjack/agents/refactoring_agent.py +516 -246
- crackerjack/agents/refactoring_helpers.py +282 -0
- crackerjack/agents/security_agent.py +393 -90
- crackerjack/agents/test_creation_agent.py +1776 -120
- crackerjack/agents/test_specialist_agent.py +59 -15
- crackerjack/agents/tracker.py +0 -102
- crackerjack/api.py +145 -37
- crackerjack/cli/handlers.py +48 -30
- crackerjack/cli/interactive.py +11 -11
- crackerjack/cli/options.py +66 -4
- crackerjack/code_cleaner.py +808 -148
- crackerjack/config/global_lock_config.py +110 -0
- crackerjack/config/hooks.py +43 -64
- crackerjack/core/async_workflow_orchestrator.py +247 -97
- crackerjack/core/autofix_coordinator.py +192 -109
- crackerjack/core/enhanced_container.py +46 -63
- crackerjack/core/file_lifecycle.py +549 -0
- crackerjack/core/performance.py +9 -8
- crackerjack/core/performance_monitor.py +395 -0
- crackerjack/core/phase_coordinator.py +281 -94
- crackerjack/core/proactive_workflow.py +9 -58
- crackerjack/core/resource_manager.py +501 -0
- crackerjack/core/service_watchdog.py +490 -0
- crackerjack/core/session_coordinator.py +4 -8
- crackerjack/core/timeout_manager.py +504 -0
- crackerjack/core/websocket_lifecycle.py +475 -0
- crackerjack/core/workflow_orchestrator.py +343 -209
- crackerjack/dynamic_config.py +50 -9
- crackerjack/errors.py +3 -4
- crackerjack/executors/async_hook_executor.py +63 -13
- crackerjack/executors/cached_hook_executor.py +14 -14
- crackerjack/executors/hook_executor.py +100 -37
- crackerjack/executors/hook_lock_manager.py +856 -0
- crackerjack/executors/individual_hook_executor.py +120 -86
- crackerjack/intelligence/__init__.py +0 -7
- crackerjack/intelligence/adaptive_learning.py +13 -86
- crackerjack/intelligence/agent_orchestrator.py +15 -78
- crackerjack/intelligence/agent_registry.py +12 -59
- crackerjack/intelligence/agent_selector.py +31 -92
- crackerjack/intelligence/integration.py +1 -41
- crackerjack/interactive.py +9 -9
- crackerjack/managers/async_hook_manager.py +25 -8
- crackerjack/managers/hook_manager.py +9 -9
- crackerjack/managers/publish_manager.py +57 -59
- crackerjack/managers/test_command_builder.py +6 -36
- crackerjack/managers/test_executor.py +9 -61
- crackerjack/managers/test_manager.py +17 -63
- crackerjack/managers/test_manager_backup.py +77 -127
- crackerjack/managers/test_progress.py +4 -23
- crackerjack/mcp/cache.py +5 -12
- crackerjack/mcp/client_runner.py +10 -10
- crackerjack/mcp/context.py +64 -6
- crackerjack/mcp/dashboard.py +14 -11
- crackerjack/mcp/enhanced_progress_monitor.py +55 -55
- crackerjack/mcp/file_monitor.py +72 -42
- crackerjack/mcp/progress_components.py +103 -84
- crackerjack/mcp/progress_monitor.py +122 -49
- crackerjack/mcp/rate_limiter.py +12 -12
- crackerjack/mcp/server_core.py +16 -22
- crackerjack/mcp/service_watchdog.py +26 -26
- crackerjack/mcp/state.py +15 -0
- crackerjack/mcp/tools/core_tools.py +95 -39
- crackerjack/mcp/tools/error_analyzer.py +6 -32
- crackerjack/mcp/tools/execution_tools.py +1 -56
- crackerjack/mcp/tools/execution_tools_backup.py +35 -131
- crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
- crackerjack/mcp/tools/intelligence_tools.py +2 -55
- crackerjack/mcp/tools/monitoring_tools.py +308 -145
- crackerjack/mcp/tools/proactive_tools.py +12 -42
- crackerjack/mcp/tools/progress_tools.py +23 -15
- crackerjack/mcp/tools/utility_tools.py +3 -40
- crackerjack/mcp/tools/workflow_executor.py +40 -60
- crackerjack/mcp/websocket/app.py +0 -3
- crackerjack/mcp/websocket/endpoints.py +206 -268
- crackerjack/mcp/websocket/jobs.py +213 -66
- crackerjack/mcp/websocket/server.py +84 -6
- crackerjack/mcp/websocket/websocket_handler.py +137 -29
- crackerjack/models/config_adapter.py +3 -16
- crackerjack/models/protocols.py +162 -3
- crackerjack/models/resource_protocols.py +454 -0
- crackerjack/models/task.py +3 -3
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +25 -71
- crackerjack/monitoring/regression_prevention.py +28 -87
- crackerjack/orchestration/advanced_orchestrator.py +44 -78
- crackerjack/orchestration/coverage_improvement.py +10 -60
- crackerjack/orchestration/execution_strategies.py +16 -16
- crackerjack/orchestration/test_progress_streamer.py +61 -53
- crackerjack/plugins/base.py +1 -1
- crackerjack/plugins/managers.py +22 -20
- crackerjack/py313.py +65 -21
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +627 -0
- crackerjack/services/cache.py +7 -9
- crackerjack/services/config.py +35 -52
- crackerjack/services/config_integrity.py +5 -16
- crackerjack/services/config_merge.py +542 -0
- crackerjack/services/contextual_ai_assistant.py +17 -19
- crackerjack/services/coverage_ratchet.py +44 -73
- crackerjack/services/debug.py +25 -39
- crackerjack/services/dependency_monitor.py +52 -50
- crackerjack/services/enhanced_filesystem.py +14 -11
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/filesystem.py +1 -12
- crackerjack/services/git.py +71 -47
- crackerjack/services/health_metrics.py +31 -27
- crackerjack/services/initialization.py +276 -428
- crackerjack/services/input_validator.py +760 -0
- crackerjack/services/log_manager.py +16 -16
- crackerjack/services/logging.py +7 -6
- crackerjack/services/metrics.py +43 -43
- crackerjack/services/pattern_cache.py +2 -31
- crackerjack/services/pattern_detector.py +26 -63
- crackerjack/services/performance_benchmarks.py +20 -45
- crackerjack/services/regex_patterns.py +2887 -0
- crackerjack/services/regex_utils.py +537 -0
- crackerjack/services/secure_path_utils.py +683 -0
- crackerjack/services/secure_status_formatter.py +534 -0
- crackerjack/services/secure_subprocess.py +605 -0
- crackerjack/services/security.py +47 -10
- crackerjack/services/security_logger.py +492 -0
- crackerjack/services/server_manager.py +109 -50
- crackerjack/services/smart_scheduling.py +8 -25
- crackerjack/services/status_authentication.py +603 -0
- crackerjack/services/status_security_manager.py +442 -0
- crackerjack/services/thread_safe_status_collector.py +546 -0
- crackerjack/services/tool_version_service.py +1 -23
- crackerjack/services/unified_config.py +36 -58
- crackerjack/services/validation_rate_limiter.py +269 -0
- crackerjack/services/version_checker.py +9 -40
- crackerjack/services/websocket_resource_limiter.py +572 -0
- crackerjack/slash_commands/__init__.py +52 -2
- crackerjack/tools/__init__.py +0 -0
- crackerjack/tools/validate_input_validator_patterns.py +262 -0
- crackerjack/tools/validate_regex_patterns.py +198 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/METADATA +197 -12
- crackerjack-0.31.13.dist-info/RECORD +178 -0
- crackerjack/cli/facade.py +0 -104
- crackerjack-0.31.10.dist-info/RECORD +0 -149
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/WHEEL +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"""Multi-Agent Execution Orchestrator.
|
|
2
|
-
|
|
3
|
-
Coordinates execution of multiple agents with smart routing, fallback strategies,
|
|
4
|
-
and result aggregation.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
1
|
import asyncio
|
|
8
2
|
import logging
|
|
9
3
|
import typing as t
|
|
@@ -22,26 +16,20 @@ from .agent_selector import AgentScore, AgentSelector, TaskDescription
|
|
|
22
16
|
|
|
23
17
|
|
|
24
18
|
class ExecutionStrategy(Enum):
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
SEQUENTIAL = "sequential" # Run agents one by one until success
|
|
30
|
-
CONSENSUS = "consensus" # Run multiple agents and compare results
|
|
19
|
+
SINGLE_BEST = "single_best"
|
|
20
|
+
PARALLEL = "parallel"
|
|
21
|
+
SEQUENTIAL = "sequential"
|
|
22
|
+
CONSENSUS = "consensus"
|
|
31
23
|
|
|
32
24
|
|
|
33
25
|
class ExecutionMode(Enum):
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
GUIDED = "guided" # With human oversight
|
|
38
|
-
ADVISORY = "advisory" # Recommendations only
|
|
26
|
+
AUTONOMOUS = "autonomous"
|
|
27
|
+
GUIDED = "guided"
|
|
28
|
+
ADVISORY = "advisory"
|
|
39
29
|
|
|
40
30
|
|
|
41
31
|
@dataclass
|
|
42
32
|
class ExecutionRequest:
|
|
43
|
-
"""Request for agent execution."""
|
|
44
|
-
|
|
45
33
|
task: TaskDescription
|
|
46
34
|
strategy: ExecutionStrategy = ExecutionStrategy.SINGLE_BEST
|
|
47
35
|
mode: ExecutionMode = ExecutionMode.AUTONOMOUS
|
|
@@ -53,11 +41,9 @@ class ExecutionRequest:
|
|
|
53
41
|
|
|
54
42
|
@dataclass
|
|
55
43
|
class ExecutionResult:
|
|
56
|
-
"""Result of agent execution."""
|
|
57
|
-
|
|
58
44
|
success: bool
|
|
59
|
-
primary_result: t.Any | None
|
|
60
|
-
all_results: list[tuple[RegisteredAgent, t.Any]]
|
|
45
|
+
primary_result: t.Any | None
|
|
46
|
+
all_results: list[tuple[RegisteredAgent, t.Any]]
|
|
61
47
|
execution_time: float
|
|
62
48
|
agents_used: list[str]
|
|
63
49
|
strategy_used: ExecutionStrategy
|
|
@@ -66,8 +52,6 @@ class ExecutionResult:
|
|
|
66
52
|
|
|
67
53
|
|
|
68
54
|
class AgentOrchestrator:
|
|
69
|
-
"""Multi-agent execution orchestrator."""
|
|
70
|
-
|
|
71
55
|
def __init__(
|
|
72
56
|
self,
|
|
73
57
|
registry: AgentRegistry | None = None,
|
|
@@ -79,7 +63,6 @@ class AgentOrchestrator:
|
|
|
79
63
|
self._execution_stats: dict[str, int] = {}
|
|
80
64
|
|
|
81
65
|
async def execute(self, request: ExecutionRequest) -> ExecutionResult:
|
|
82
|
-
"""Execute a request using the intelligent agent system."""
|
|
83
66
|
start_time = asyncio.get_event_loop().time()
|
|
84
67
|
|
|
85
68
|
try:
|
|
@@ -88,7 +71,6 @@ class AgentOrchestrator:
|
|
|
88
71
|
f"(strategy: {request.strategy.value})"
|
|
89
72
|
)
|
|
90
73
|
|
|
91
|
-
# Get agent candidates
|
|
92
74
|
candidates = await self.selector.select_agents(
|
|
93
75
|
request.task, max_candidates=request.max_agents
|
|
94
76
|
)
|
|
@@ -100,7 +82,6 @@ class AgentOrchestrator:
|
|
|
100
82
|
request.strategy,
|
|
101
83
|
)
|
|
102
84
|
|
|
103
|
-
# Execute based on strategy
|
|
104
85
|
if request.strategy == ExecutionStrategy.SINGLE_BEST:
|
|
105
86
|
result = await self._execute_single_best(request, candidates)
|
|
106
87
|
elif request.strategy == ExecutionStrategy.PARALLEL:
|
|
@@ -115,14 +96,13 @@ class AgentOrchestrator:
|
|
|
115
96
|
execution_time = asyncio.get_event_loop().time() - start_time
|
|
116
97
|
result.execution_time = execution_time
|
|
117
98
|
|
|
118
|
-
# Update stats
|
|
119
99
|
for agent_name in result.agents_used:
|
|
120
100
|
self._execution_stats[agent_name] = (
|
|
121
101
|
self._execution_stats.get(agent_name, 0) + 1
|
|
122
102
|
)
|
|
123
103
|
|
|
124
104
|
self.logger.info(
|
|
125
|
-
f"Execution completed in {execution_time
|
|
105
|
+
f"Execution completed in {execution_time: .2f}s: "
|
|
126
106
|
f"{'success' if result.success else 'failure'} "
|
|
127
107
|
f"using {len(result.agents_used)} agents"
|
|
128
108
|
)
|
|
@@ -142,7 +122,6 @@ class AgentOrchestrator:
|
|
|
142
122
|
request: ExecutionRequest,
|
|
143
123
|
candidates: list[AgentScore],
|
|
144
124
|
) -> ExecutionResult:
|
|
145
|
-
"""Execute using the single best agent."""
|
|
146
125
|
best_candidate = candidates[0]
|
|
147
126
|
|
|
148
127
|
try:
|
|
@@ -152,14 +131,13 @@ class AgentOrchestrator:
|
|
|
152
131
|
success=True,
|
|
153
132
|
primary_result=result,
|
|
154
133
|
all_results=[(best_candidate.agent, result)],
|
|
155
|
-
execution_time=0.0,
|
|
134
|
+
execution_time=0.0,
|
|
156
135
|
agents_used=[best_candidate.agent.metadata.name],
|
|
157
136
|
strategy_used=ExecutionStrategy.SINGLE_BEST,
|
|
158
137
|
recommendations=self._generate_recommendations(best_candidate),
|
|
159
138
|
)
|
|
160
139
|
|
|
161
140
|
except Exception as e:
|
|
162
|
-
# Fallback to next best agent if available
|
|
163
141
|
if len(candidates) > 1 and request.fallback_to_system:
|
|
164
142
|
self.logger.warning(
|
|
165
143
|
f"Primary agent {best_candidate.agent.metadata.name} failed: {e}. "
|
|
@@ -172,7 +150,7 @@ class AgentOrchestrator:
|
|
|
172
150
|
mode=request.mode,
|
|
173
151
|
max_agents=len(candidates) - 1,
|
|
174
152
|
timeout_seconds=request.timeout_seconds,
|
|
175
|
-
fallback_to_system=False,
|
|
153
|
+
fallback_to_system=False,
|
|
176
154
|
context=request.context,
|
|
177
155
|
)
|
|
178
156
|
|
|
@@ -193,7 +171,6 @@ class AgentOrchestrator:
|
|
|
193
171
|
request: ExecutionRequest,
|
|
194
172
|
candidates: list[AgentScore],
|
|
195
173
|
) -> ExecutionResult:
|
|
196
|
-
"""Execute multiple agents in parallel."""
|
|
197
174
|
tasks = []
|
|
198
175
|
agents_to_execute = candidates[: request.max_agents]
|
|
199
176
|
|
|
@@ -203,7 +180,6 @@ class AgentOrchestrator:
|
|
|
203
180
|
)
|
|
204
181
|
tasks.append((candidate.agent, task))
|
|
205
182
|
|
|
206
|
-
# Wait for all tasks to complete
|
|
207
183
|
results = []
|
|
208
184
|
successful_results = []
|
|
209
185
|
|
|
@@ -218,12 +194,10 @@ class AgentOrchestrator:
|
|
|
218
194
|
except Exception as e:
|
|
219
195
|
results.append((agent, e))
|
|
220
196
|
|
|
221
|
-
# Choose best successful result
|
|
222
197
|
primary_result = None
|
|
223
198
|
agents_used = []
|
|
224
199
|
|
|
225
200
|
if successful_results:
|
|
226
|
-
# Use result from highest-priority agent
|
|
227
201
|
successful_results.sort(key=lambda x: x[0].metadata.priority, reverse=True)
|
|
228
202
|
primary_result = successful_results[0][1]
|
|
229
203
|
agents_used = [agent.metadata.name for agent, _ in successful_results]
|
|
@@ -243,7 +217,6 @@ class AgentOrchestrator:
|
|
|
243
217
|
request: ExecutionRequest,
|
|
244
218
|
candidates: list[AgentScore],
|
|
245
219
|
) -> ExecutionResult:
|
|
246
|
-
"""Execute agents sequentially until one succeeds."""
|
|
247
220
|
results = []
|
|
248
221
|
|
|
249
222
|
for candidate in candidates[: request.max_agents]:
|
|
@@ -255,7 +228,6 @@ class AgentOrchestrator:
|
|
|
255
228
|
|
|
256
229
|
results.append((candidate.agent, result))
|
|
257
230
|
|
|
258
|
-
# Success - return immediately
|
|
259
231
|
return ExecutionResult(
|
|
260
232
|
success=True,
|
|
261
233
|
primary_result=result,
|
|
@@ -273,7 +245,6 @@ class AgentOrchestrator:
|
|
|
273
245
|
)
|
|
274
246
|
continue
|
|
275
247
|
|
|
276
|
-
# All agents failed
|
|
277
248
|
return ExecutionResult(
|
|
278
249
|
success=False,
|
|
279
250
|
primary_result=None,
|
|
@@ -289,13 +260,11 @@ class AgentOrchestrator:
|
|
|
289
260
|
request: ExecutionRequest,
|
|
290
261
|
candidates: list[AgentScore],
|
|
291
262
|
) -> ExecutionResult:
|
|
292
|
-
"""Execute multiple agents and build consensus from results."""
|
|
293
|
-
# First run parallel execution
|
|
294
263
|
parallel_request = ExecutionRequest(
|
|
295
264
|
task=request.task,
|
|
296
265
|
strategy=ExecutionStrategy.PARALLEL,
|
|
297
266
|
mode=request.mode,
|
|
298
|
-
max_agents=min(request.max_agents, 3),
|
|
267
|
+
max_agents=min(request.max_agents, 3),
|
|
299
268
|
timeout_seconds=request.timeout_seconds,
|
|
300
269
|
fallback_to_system=False,
|
|
301
270
|
context=request.context,
|
|
@@ -306,7 +275,6 @@ class AgentOrchestrator:
|
|
|
306
275
|
if not parallel_result.success:
|
|
307
276
|
return parallel_result
|
|
308
277
|
|
|
309
|
-
# Analyze results for consensus
|
|
310
278
|
successful_results = [
|
|
311
279
|
(agent, result)
|
|
312
280
|
for agent, result in parallel_result.all_results
|
|
@@ -314,10 +282,8 @@ class AgentOrchestrator:
|
|
|
314
282
|
]
|
|
315
283
|
|
|
316
284
|
if len(successful_results) < 2:
|
|
317
|
-
# Not enough results for consensus - return best result
|
|
318
285
|
return parallel_result
|
|
319
286
|
|
|
320
|
-
# Build consensus (simplified - could be much more sophisticated)
|
|
321
287
|
consensus_result = self._build_consensus(successful_results)
|
|
322
288
|
|
|
323
289
|
return ExecutionResult(
|
|
@@ -333,15 +299,11 @@ class AgentOrchestrator:
|
|
|
333
299
|
async def _execute_agent(
|
|
334
300
|
self, agent: RegisteredAgent, request: ExecutionRequest
|
|
335
301
|
) -> t.Any:
|
|
336
|
-
"""Execute a specific agent."""
|
|
337
302
|
if agent.agent is not None:
|
|
338
|
-
# Crackerjack agent
|
|
339
303
|
return await self._execute_crackerjack_agent(agent, request)
|
|
340
304
|
elif agent.agent_path is not None:
|
|
341
|
-
# User agent
|
|
342
305
|
return await self._execute_user_agent(agent, request)
|
|
343
306
|
elif agent.subagent_type is not None:
|
|
344
|
-
# System agent
|
|
345
307
|
return await self._execute_system_agent(agent, request)
|
|
346
308
|
else:
|
|
347
309
|
raise ValueError(f"Invalid agent configuration: {agent.metadata.name}")
|
|
@@ -349,7 +311,6 @@ class AgentOrchestrator:
|
|
|
349
311
|
async def _execute_agent_safe(
|
|
350
312
|
self, agent: RegisteredAgent, request: ExecutionRequest
|
|
351
313
|
) -> t.Any:
|
|
352
|
-
"""Execute an agent with exception handling."""
|
|
353
314
|
try:
|
|
354
315
|
return await self._execute_agent(agent, request)
|
|
355
316
|
except Exception as e:
|
|
@@ -360,20 +321,17 @@ class AgentOrchestrator:
|
|
|
360
321
|
agent: RegisteredAgent,
|
|
361
322
|
request: ExecutionRequest,
|
|
362
323
|
) -> t.Any:
|
|
363
|
-
"""Execute a built-in crackerjack agent."""
|
|
364
324
|
if not agent.agent:
|
|
365
325
|
raise ValueError("No crackerjack agent instance available")
|
|
366
326
|
|
|
367
|
-
# Convert task to Issue for crackerjack agents
|
|
368
327
|
issue = Issue(
|
|
369
328
|
id="orchestrated_task",
|
|
370
329
|
type=self._map_task_to_issue_type(request.task),
|
|
371
330
|
severity=self._map_task_priority_to_severity(request.task),
|
|
372
331
|
message=request.task.description,
|
|
373
|
-
file_path=None,
|
|
332
|
+
file_path=None,
|
|
374
333
|
)
|
|
375
334
|
|
|
376
|
-
# Execute agent
|
|
377
335
|
result = await agent.agent.analyze_and_fix(issue)
|
|
378
336
|
return result
|
|
379
337
|
|
|
@@ -382,11 +340,8 @@ class AgentOrchestrator:
|
|
|
382
340
|
agent: RegisteredAgent,
|
|
383
341
|
request: ExecutionRequest,
|
|
384
342
|
) -> t.Any:
|
|
385
|
-
"""Execute a user agent via Task tool."""
|
|
386
|
-
# Import Task tool dynamically to avoid circular imports
|
|
387
343
|
from crackerjack.mcp.tools.core_tools import create_task_with_subagent
|
|
388
344
|
|
|
389
|
-
# Use Task tool to execute user agent
|
|
390
345
|
result = await create_task_with_subagent(
|
|
391
346
|
description=f"Execute task using {agent.metadata.name}",
|
|
392
347
|
prompt=request.task.description,
|
|
@@ -400,14 +355,11 @@ class AgentOrchestrator:
|
|
|
400
355
|
agent: RegisteredAgent,
|
|
401
356
|
request: ExecutionRequest,
|
|
402
357
|
) -> t.Any:
|
|
403
|
-
"""Execute a system agent via Task tool."""
|
|
404
358
|
if not agent.subagent_type:
|
|
405
359
|
raise ValueError("No subagent type specified for system agent")
|
|
406
360
|
|
|
407
|
-
# Import Task tool dynamically
|
|
408
361
|
from crackerjack.mcp.tools.core_tools import create_task_with_subagent
|
|
409
362
|
|
|
410
|
-
# Use Task tool to execute system agent
|
|
411
363
|
result = await create_task_with_subagent(
|
|
412
364
|
description=f"Execute task using {agent.metadata.name}",
|
|
413
365
|
prompt=request.task.description,
|
|
@@ -417,11 +369,8 @@ class AgentOrchestrator:
|
|
|
417
369
|
return result
|
|
418
370
|
|
|
419
371
|
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
372
|
from crackerjack.agents.base import IssueType
|
|
423
373
|
|
|
424
|
-
# Simple mapping - could be more sophisticated
|
|
425
374
|
context_map = {
|
|
426
375
|
"code_quality": IssueType.FORMATTING,
|
|
427
376
|
"refactoring": IssueType.COMPLEXITY,
|
|
@@ -434,7 +383,6 @@ class AgentOrchestrator:
|
|
|
434
383
|
if task.context and task.context.value in context_map:
|
|
435
384
|
return context_map[task.context.value]
|
|
436
385
|
|
|
437
|
-
# Analyze task description for hints
|
|
438
386
|
desc_lower = task.description.lower()
|
|
439
387
|
if "test" in desc_lower:
|
|
440
388
|
return IssueType.TEST_FAILURE
|
|
@@ -445,10 +393,9 @@ class AgentOrchestrator:
|
|
|
445
393
|
elif "format" in desc_lower:
|
|
446
394
|
return IssueType.FORMATTING
|
|
447
395
|
|
|
448
|
-
return IssueType.FORMATTING
|
|
396
|
+
return IssueType.FORMATTING
|
|
449
397
|
|
|
450
398
|
def _map_task_priority_to_severity(self, task: TaskDescription):
|
|
451
|
-
"""Map task priority to Issue severity."""
|
|
452
399
|
from crackerjack.agents.base import Priority
|
|
453
400
|
|
|
454
401
|
if task.priority >= 80:
|
|
@@ -459,14 +406,10 @@ class AgentOrchestrator:
|
|
|
459
406
|
return Priority.LOW
|
|
460
407
|
|
|
461
408
|
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
409
|
results.sort(key=lambda x: x[0].metadata.priority, reverse=True)
|
|
466
410
|
return results[0][1]
|
|
467
411
|
|
|
468
412
|
def _generate_recommendations(self, candidate: AgentScore) -> list[str]:
|
|
469
|
-
"""Generate recommendations based on agent selection."""
|
|
470
413
|
recommendations = []
|
|
471
414
|
|
|
472
415
|
if candidate.final_score > 0.8:
|
|
@@ -491,7 +434,6 @@ class AgentOrchestrator:
|
|
|
491
434
|
start_time: float,
|
|
492
435
|
strategy: ExecutionStrategy,
|
|
493
436
|
) -> ExecutionResult:
|
|
494
|
-
"""Create an error result."""
|
|
495
437
|
execution_time = asyncio.get_event_loop().time() - start_time
|
|
496
438
|
|
|
497
439
|
return ExecutionResult(
|
|
@@ -505,7 +447,6 @@ class AgentOrchestrator:
|
|
|
505
447
|
)
|
|
506
448
|
|
|
507
449
|
def get_execution_stats(self) -> dict[str, t.Any]:
|
|
508
|
-
"""Get execution statistics."""
|
|
509
450
|
from operator import itemgetter
|
|
510
451
|
|
|
511
452
|
return {
|
|
@@ -519,10 +460,8 @@ class AgentOrchestrator:
|
|
|
519
460
|
}
|
|
520
461
|
|
|
521
462
|
async def analyze_task_routing(self, task: TaskDescription) -> dict[str, t.Any]:
|
|
522
|
-
"""Analyze how a task would be routed through the system."""
|
|
523
463
|
analysis = await self.selector.analyze_task_complexity(task)
|
|
524
464
|
|
|
525
|
-
# Add orchestration recommendations
|
|
526
465
|
if analysis["complexity_level"] == "high":
|
|
527
466
|
analysis["recommended_strategy"] = ExecutionStrategy.CONSENSUS
|
|
528
467
|
elif analysis["candidate_count"] > 3:
|
|
@@ -535,12 +474,10 @@ class AgentOrchestrator:
|
|
|
535
474
|
return analysis
|
|
536
475
|
|
|
537
476
|
|
|
538
|
-
# Global orchestrator instance
|
|
539
477
|
_orchestrator_instance: AgentOrchestrator | None = None
|
|
540
478
|
|
|
541
479
|
|
|
542
480
|
async def get_agent_orchestrator() -> AgentOrchestrator:
|
|
543
|
-
"""Get or create the global agent orchestrator."""
|
|
544
481
|
global _orchestrator_instance
|
|
545
482
|
|
|
546
483
|
if _orchestrator_instance is None:
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"""Intelligent Agent Registry System.
|
|
2
|
-
|
|
3
|
-
Unifies access to crackerjack agents, user agents, and system agents with smart
|
|
4
|
-
prioritization and capability mapping.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
1
|
import logging
|
|
8
2
|
import typing as t
|
|
9
3
|
from dataclasses import dataclass
|
|
@@ -14,16 +8,12 @@ from crackerjack.agents.base import SubAgent, agent_registry
|
|
|
14
8
|
|
|
15
9
|
|
|
16
10
|
class AgentSource(Enum):
|
|
17
|
-
"""Source of an agent."""
|
|
18
|
-
|
|
19
11
|
CRACKERJACK = "crackerjack"
|
|
20
12
|
USER = "user"
|
|
21
13
|
SYSTEM = "system"
|
|
22
14
|
|
|
23
15
|
|
|
24
16
|
class AgentCapability(Enum):
|
|
25
|
-
"""Agent capability categories."""
|
|
26
|
-
|
|
27
17
|
ARCHITECTURE = "architecture"
|
|
28
18
|
REFACTORING = "refactoring"
|
|
29
19
|
TESTING = "testing"
|
|
@@ -38,13 +28,11 @@ class AgentCapability(Enum):
|
|
|
38
28
|
|
|
39
29
|
@dataclass
|
|
40
30
|
class AgentMetadata:
|
|
41
|
-
"""Metadata about an agent."""
|
|
42
|
-
|
|
43
31
|
name: str
|
|
44
32
|
source: AgentSource
|
|
45
33
|
capabilities: set[AgentCapability]
|
|
46
|
-
priority: int
|
|
47
|
-
confidence_factor: float
|
|
34
|
+
priority: int
|
|
35
|
+
confidence_factor: float
|
|
48
36
|
description: str
|
|
49
37
|
model: str | None = None
|
|
50
38
|
tags: list[str] | None = None
|
|
@@ -52,17 +40,13 @@ class AgentMetadata:
|
|
|
52
40
|
|
|
53
41
|
@dataclass
|
|
54
42
|
class RegisteredAgent:
|
|
55
|
-
"""A registered agent with metadata and access."""
|
|
56
|
-
|
|
57
43
|
metadata: AgentMetadata
|
|
58
|
-
agent: SubAgent | None = None
|
|
59
|
-
agent_path: Path | None = None
|
|
60
|
-
subagent_type: str | None = None
|
|
44
|
+
agent: SubAgent | None = None
|
|
45
|
+
agent_path: Path | None = None
|
|
46
|
+
subagent_type: str | None = None
|
|
61
47
|
|
|
62
48
|
|
|
63
49
|
class AgentRegistry:
|
|
64
|
-
"""Registry of all available agents across all systems."""
|
|
65
|
-
|
|
66
50
|
def __init__(self) -> None:
|
|
67
51
|
self.logger = logging.getLogger(__name__)
|
|
68
52
|
self._agents: dict[str, RegisteredAgent] = {}
|
|
@@ -70,15 +54,12 @@ class AgentRegistry:
|
|
|
70
54
|
self._user_agent_cache: dict[str, dict[str, t.Any]] = {}
|
|
71
55
|
|
|
72
56
|
async def initialize(self) -> None:
|
|
73
|
-
"""Initialize the registry with all available agents."""
|
|
74
57
|
self.logger.info("Initializing Intelligent Agent Registry")
|
|
75
58
|
|
|
76
|
-
# Load agents in priority order
|
|
77
59
|
await self._register_crackerjack_agents()
|
|
78
60
|
await self._register_user_agents()
|
|
79
61
|
await self._register_system_agents()
|
|
80
62
|
|
|
81
|
-
# Build capability mapping
|
|
82
63
|
self._build_capability_map()
|
|
83
64
|
|
|
84
65
|
self.logger.info(
|
|
@@ -89,13 +70,10 @@ class AgentRegistry:
|
|
|
89
70
|
)
|
|
90
71
|
|
|
91
72
|
async def _register_crackerjack_agents(self) -> None:
|
|
92
|
-
"""Register built-in crackerjack agents."""
|
|
93
73
|
self.logger.debug("Registering crackerjack agents")
|
|
94
74
|
|
|
95
|
-
# Get all registered crackerjack agents
|
|
96
75
|
from crackerjack.agents.base import AgentContext
|
|
97
76
|
|
|
98
|
-
# Create a dummy context to get agents
|
|
99
77
|
context = AgentContext(
|
|
100
78
|
project_path=Path.cwd(),
|
|
101
79
|
)
|
|
@@ -109,7 +87,7 @@ class AgentRegistry:
|
|
|
109
87
|
name=agent.name,
|
|
110
88
|
source=AgentSource.CRACKERJACK,
|
|
111
89
|
capabilities=capabilities,
|
|
112
|
-
priority=100,
|
|
90
|
+
priority=100,
|
|
113
91
|
confidence_factor=1.0,
|
|
114
92
|
description=f"Built-in crackerjack {agent.__class__.__name__}",
|
|
115
93
|
)
|
|
@@ -123,7 +101,6 @@ class AgentRegistry:
|
|
|
123
101
|
self.logger.debug(f"Registered crackerjack agent: {agent.name}")
|
|
124
102
|
|
|
125
103
|
async def _register_user_agents(self) -> None:
|
|
126
|
-
"""Register user agents from ~/.claude/agents/."""
|
|
127
104
|
self.logger.debug("Registering user agents")
|
|
128
105
|
|
|
129
106
|
user_agents_dir = Path.home() / ".claude" / "agents"
|
|
@@ -141,7 +118,7 @@ class AgentRegistry:
|
|
|
141
118
|
name=agent_data["name"],
|
|
142
119
|
source=AgentSource.USER,
|
|
143
120
|
capabilities=capabilities,
|
|
144
|
-
priority=80,
|
|
121
|
+
priority=80,
|
|
145
122
|
confidence_factor=0.9,
|
|
146
123
|
description=agent_data.get("description", "User agent"),
|
|
147
124
|
model=agent_data.get("model"),
|
|
@@ -161,10 +138,8 @@ class AgentRegistry:
|
|
|
161
138
|
self.logger.warning(f"Failed to parse user agent {agent_file}: {e}")
|
|
162
139
|
|
|
163
140
|
async def _register_system_agents(self) -> None:
|
|
164
|
-
"""Register built-in system agents from Task tool."""
|
|
165
141
|
self.logger.debug("Registering system agents")
|
|
166
142
|
|
|
167
|
-
# Known system agents from Task tool
|
|
168
143
|
system_agents = [
|
|
169
144
|
(
|
|
170
145
|
"general-purpose",
|
|
@@ -186,7 +161,7 @@ class AgentRegistry:
|
|
|
186
161
|
name=agent_name,
|
|
187
162
|
source=AgentSource.SYSTEM,
|
|
188
163
|
capabilities=capabilities,
|
|
189
|
-
priority=60,
|
|
164
|
+
priority=60,
|
|
190
165
|
confidence_factor=0.7,
|
|
191
166
|
description=description,
|
|
192
167
|
)
|
|
@@ -200,7 +175,6 @@ class AgentRegistry:
|
|
|
200
175
|
self.logger.debug(f"Registered system agent: {agent_name}")
|
|
201
176
|
|
|
202
177
|
async def _parse_user_agent_file(self, agent_file: Path) -> dict[str, t.Any] | None:
|
|
203
|
-
"""Parse a user agent markdown file."""
|
|
204
178
|
try:
|
|
205
179
|
content = agent_file.read_text(encoding="utf-8")
|
|
206
180
|
return self._extract_agent_data_from_content(content)
|
|
@@ -209,8 +183,7 @@ class AgentRegistry:
|
|
|
209
183
|
return None
|
|
210
184
|
|
|
211
185
|
def _extract_agent_data_from_content(self, content: str) -> dict[str, t.Any] | None:
|
|
212
|
-
|
|
213
|
-
if not content.startswith("---\n"):
|
|
186
|
+
if not content.startswith("- --\n"):
|
|
214
187
|
return None
|
|
215
188
|
|
|
216
189
|
lines = content.split("\n")
|
|
@@ -222,30 +195,26 @@ class AgentRegistry:
|
|
|
222
195
|
return self._build_agent_data(lines, yaml_end)
|
|
223
196
|
|
|
224
197
|
def _find_yaml_end_marker(self, lines: list[str]) -> int:
|
|
225
|
-
"""Find the end marker for YAML frontmatter."""
|
|
226
198
|
for i, line in enumerate(lines[1:], 1):
|
|
227
|
-
if line == "
|
|
199
|
+
if line == "- --":
|
|
228
200
|
return i
|
|
229
201
|
return -1
|
|
230
202
|
|
|
231
203
|
def _build_agent_data(self, lines: list[str], yaml_end: int) -> dict[str, t.Any]:
|
|
232
|
-
"""Build agent data from parsed lines."""
|
|
233
204
|
yaml_lines = lines[1:yaml_end]
|
|
234
205
|
agent_data = {}
|
|
235
206
|
|
|
236
207
|
for line in yaml_lines:
|
|
237
|
-
if ":" in line:
|
|
238
|
-
key, value = line.split(":", 1)
|
|
208
|
+
if ": " in line:
|
|
209
|
+
key, value = line.split(": ", 1)
|
|
239
210
|
agent_data[key.strip()] = value.strip()
|
|
240
211
|
|
|
241
212
|
agent_data["content"] = "\n".join(lines[yaml_end + 1 :])
|
|
242
213
|
return agent_data
|
|
243
214
|
|
|
244
215
|
def _infer_capabilities_from_agent(self, agent: SubAgent) -> set[AgentCapability]:
|
|
245
|
-
"""Infer capabilities from a crackerjack agent."""
|
|
246
216
|
capabilities = set()
|
|
247
217
|
|
|
248
|
-
# Map agent class names to capabilities
|
|
249
218
|
class_name = agent.__class__.__name__.lower()
|
|
250
219
|
|
|
251
220
|
if "architect" in class_name:
|
|
@@ -269,7 +238,6 @@ class AgentRegistry:
|
|
|
269
238
|
if "dry" in class_name:
|
|
270
239
|
capabilities.add(AgentCapability.REFACTORING)
|
|
271
240
|
|
|
272
|
-
# Fallback to general code analysis
|
|
273
241
|
if not capabilities:
|
|
274
242
|
capabilities.add(AgentCapability.CODE_ANALYSIS)
|
|
275
243
|
|
|
@@ -278,7 +246,6 @@ class AgentRegistry:
|
|
|
278
246
|
def _infer_capabilities_from_user_agent(
|
|
279
247
|
self, agent_data: dict[str, t.Any]
|
|
280
248
|
) -> set[AgentCapability]:
|
|
281
|
-
"""Infer capabilities from user agent metadata."""
|
|
282
249
|
capabilities = set()
|
|
283
250
|
|
|
284
251
|
name = agent_data.get("name", "").lower()
|
|
@@ -287,7 +254,6 @@ class AgentRegistry:
|
|
|
287
254
|
|
|
288
255
|
text = f"{name} {description} {content}"
|
|
289
256
|
|
|
290
|
-
# Keyword mapping
|
|
291
257
|
keyword_map = {
|
|
292
258
|
AgentCapability.ARCHITECTURE: [
|
|
293
259
|
"architect",
|
|
@@ -320,7 +286,6 @@ class AgentRegistry:
|
|
|
320
286
|
if any(keyword in text for keyword in keywords):
|
|
321
287
|
capabilities.add(capability)
|
|
322
288
|
|
|
323
|
-
# Fallback
|
|
324
289
|
if not capabilities:
|
|
325
290
|
capabilities.add(AgentCapability.CODE_ANALYSIS)
|
|
326
291
|
|
|
@@ -329,7 +294,6 @@ class AgentRegistry:
|
|
|
329
294
|
def _infer_capabilities_from_system_agent(
|
|
330
295
|
self, name: str, description: str
|
|
331
296
|
) -> set[AgentCapability]:
|
|
332
|
-
"""Infer capabilities from system agent."""
|
|
333
297
|
capabilities = set()
|
|
334
298
|
|
|
335
299
|
text = f"{name} {description}".lower()
|
|
@@ -341,14 +305,12 @@ class AgentRegistry:
|
|
|
341
305
|
if "output" in text or "style" in text:
|
|
342
306
|
capabilities.add(AgentCapability.FORMATTING)
|
|
343
307
|
|
|
344
|
-
# Fallback
|
|
345
308
|
if not capabilities:
|
|
346
309
|
capabilities.add(AgentCapability.CODE_ANALYSIS)
|
|
347
310
|
|
|
348
311
|
return capabilities
|
|
349
312
|
|
|
350
313
|
def _build_capability_map(self) -> None:
|
|
351
|
-
"""Build mapping from capabilities to agent names."""
|
|
352
314
|
self._capability_map.clear()
|
|
353
315
|
|
|
354
316
|
for agent_name, registered_agent in self._agents.items():
|
|
@@ -357,7 +319,6 @@ class AgentRegistry:
|
|
|
357
319
|
self._capability_map[capability] = []
|
|
358
320
|
self._capability_map[capability].append(agent_name)
|
|
359
321
|
|
|
360
|
-
# Sort by priority within each capability
|
|
361
322
|
for agent_names in self._capability_map.values():
|
|
362
323
|
agent_names.sort(
|
|
363
324
|
key=lambda name: self._agents[name].metadata.priority, reverse=True
|
|
@@ -366,36 +327,30 @@ class AgentRegistry:
|
|
|
366
327
|
def get_agents_by_capability(
|
|
367
328
|
self, capability: AgentCapability
|
|
368
329
|
) -> list[RegisteredAgent]:
|
|
369
|
-
"""Get agents that have a specific capability, sorted by priority."""
|
|
370
330
|
agent_names = self._capability_map.get(capability, [])
|
|
371
331
|
return [self._agents[name] for name in agent_names]
|
|
372
332
|
|
|
373
333
|
def get_agent_by_name(self, name: str) -> RegisteredAgent | None:
|
|
374
|
-
"""Get a specific agent by name."""
|
|
375
334
|
return self._agents.get(name)
|
|
376
335
|
|
|
377
336
|
def list_all_agents(self) -> list[RegisteredAgent]:
|
|
378
|
-
"""List all registered agents, sorted by priority."""
|
|
379
337
|
agents = list(self._agents.values())
|
|
380
338
|
agents.sort(key=lambda a: a.metadata.priority, reverse=True)
|
|
381
339
|
return agents
|
|
382
340
|
|
|
383
341
|
def get_agent_stats(self) -> dict[str, t.Any]:
|
|
384
|
-
"""Get statistics about registered agents."""
|
|
385
342
|
stats: dict[str, t.Any] = {
|
|
386
343
|
"total_agents": len(self._agents),
|
|
387
344
|
"by_source": {},
|
|
388
345
|
"by_capability": {},
|
|
389
346
|
}
|
|
390
347
|
|
|
391
|
-
# Count by source
|
|
392
348
|
for source in AgentSource:
|
|
393
349
|
count = len(
|
|
394
350
|
[a for a in self._agents.values() if a.metadata.source == source]
|
|
395
351
|
)
|
|
396
352
|
stats["by_source"][source.value] = count
|
|
397
353
|
|
|
398
|
-
# Count by capability
|
|
399
354
|
for capability in AgentCapability:
|
|
400
355
|
count = len(self._capability_map.get(capability, []))
|
|
401
356
|
stats["by_capability"][capability.value] = count
|
|
@@ -403,12 +358,10 @@ class AgentRegistry:
|
|
|
403
358
|
return stats
|
|
404
359
|
|
|
405
360
|
|
|
406
|
-
# Global registry instance
|
|
407
361
|
agent_registry_instance = AgentRegistry()
|
|
408
362
|
|
|
409
363
|
|
|
410
364
|
async def get_agent_registry() -> AgentRegistry:
|
|
411
|
-
"""Get the initialized agent registry."""
|
|
412
365
|
if not agent_registry_instance._agents:
|
|
413
366
|
await agent_registry_instance.initialize()
|
|
414
367
|
return agent_registry_instance
|