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,502 @@
|
|
|
1
|
+
"""Smart Agent Selection Engine.
|
|
2
|
+
|
|
3
|
+
Intelligently selects the best agents for tasks based on context analysis,
|
|
4
|
+
capability matching, and priority scoring.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import re
|
|
9
|
+
import typing as t
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from enum import Enum
|
|
12
|
+
|
|
13
|
+
from .agent_registry import (
|
|
14
|
+
AgentCapability,
|
|
15
|
+
AgentRegistry,
|
|
16
|
+
RegisteredAgent,
|
|
17
|
+
get_agent_registry,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TaskContext(Enum):
|
|
22
|
+
"""Context categories for tasks."""
|
|
23
|
+
|
|
24
|
+
CODE_QUALITY = "code_quality"
|
|
25
|
+
ARCHITECTURE = "architecture"
|
|
26
|
+
TESTING = "testing"
|
|
27
|
+
REFACTORING = "refactoring"
|
|
28
|
+
DOCUMENTATION = "documentation"
|
|
29
|
+
SECURITY = "security"
|
|
30
|
+
PERFORMANCE = "performance"
|
|
31
|
+
DEBUGGING = "debugging"
|
|
32
|
+
PROJECT_SETUP = "project_setup"
|
|
33
|
+
GENERAL = "general"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class TaskDescription:
|
|
38
|
+
"""Description of a task to be executed."""
|
|
39
|
+
|
|
40
|
+
description: str
|
|
41
|
+
context: TaskContext | None = None
|
|
42
|
+
keywords: list[str] | None = None
|
|
43
|
+
file_patterns: list[str] | None = None
|
|
44
|
+
error_types: list[str] | None = None
|
|
45
|
+
priority: int = 50 # 0-100, higher is more important
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class AgentScore:
|
|
50
|
+
"""Score for an agent's suitability for a task."""
|
|
51
|
+
|
|
52
|
+
agent: RegisteredAgent
|
|
53
|
+
base_score: float # 0-1 based on capability match
|
|
54
|
+
context_score: float # 0-1 based on context match
|
|
55
|
+
priority_bonus: float # 0-1 based on agent priority
|
|
56
|
+
confidence_factor: float # Multiplier from agent metadata
|
|
57
|
+
final_score: float # Combined weighted score
|
|
58
|
+
reasoning: str # Explanation of the score
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class AgentSelector:
|
|
62
|
+
"""Intelligent agent selection engine."""
|
|
63
|
+
|
|
64
|
+
def __init__(self, registry: AgentRegistry | None = None) -> None:
|
|
65
|
+
self.logger = logging.getLogger(__name__)
|
|
66
|
+
self.registry = registry
|
|
67
|
+
self._task_patterns: dict[str, list[AgentCapability]] = {}
|
|
68
|
+
self._initialize_task_patterns()
|
|
69
|
+
|
|
70
|
+
def _initialize_task_patterns(self) -> None:
|
|
71
|
+
"""Initialize patterns for task analysis."""
|
|
72
|
+
self._task_patterns = {
|
|
73
|
+
# Architecture patterns
|
|
74
|
+
r"architect|design|structure|pattern|refactor.*complex": [
|
|
75
|
+
AgentCapability.ARCHITECTURE,
|
|
76
|
+
AgentCapability.REFACTORING,
|
|
77
|
+
],
|
|
78
|
+
# Code quality patterns
|
|
79
|
+
r"refurb|ruff|format|lint|style|clean.*code": [
|
|
80
|
+
AgentCapability.FORMATTING,
|
|
81
|
+
AgentCapability.CODE_ANALYSIS,
|
|
82
|
+
],
|
|
83
|
+
# Testing patterns
|
|
84
|
+
r"test|pytest|coverage|mock|fixture": [
|
|
85
|
+
AgentCapability.TESTING,
|
|
86
|
+
],
|
|
87
|
+
# Security patterns
|
|
88
|
+
r"security|vulnerability|audit|bandit|safe": [
|
|
89
|
+
AgentCapability.SECURITY,
|
|
90
|
+
],
|
|
91
|
+
# Performance patterns
|
|
92
|
+
r"performance|optimize|speed|efficient|complexity": [
|
|
93
|
+
AgentCapability.PERFORMANCE,
|
|
94
|
+
AgentCapability.CODE_ANALYSIS,
|
|
95
|
+
],
|
|
96
|
+
# Documentation patterns
|
|
97
|
+
r"document|readme|comment|explain|changelog": [
|
|
98
|
+
AgentCapability.DOCUMENTATION,
|
|
99
|
+
],
|
|
100
|
+
# Refactoring patterns
|
|
101
|
+
r"refactor|improve|simplify|dry.*violation": [
|
|
102
|
+
AgentCapability.REFACTORING,
|
|
103
|
+
AgentCapability.CODE_ANALYSIS,
|
|
104
|
+
],
|
|
105
|
+
# Debugging patterns
|
|
106
|
+
r"debug|fix|error|bug|failure": [
|
|
107
|
+
AgentCapability.DEBUGGING,
|
|
108
|
+
AgentCapability.CODE_ANALYSIS,
|
|
109
|
+
],
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async def get_registry(self) -> AgentRegistry:
|
|
113
|
+
"""Get or initialize the agent registry."""
|
|
114
|
+
if self.registry is None:
|
|
115
|
+
self.registry = await get_agent_registry()
|
|
116
|
+
return self.registry
|
|
117
|
+
|
|
118
|
+
async def select_best_agent(
|
|
119
|
+
self,
|
|
120
|
+
task: TaskDescription,
|
|
121
|
+
max_candidates: int = 5,
|
|
122
|
+
) -> AgentScore | None:
|
|
123
|
+
"""Select the single best agent for a task."""
|
|
124
|
+
candidates = await self.select_agents(task, max_candidates)
|
|
125
|
+
return candidates[0] if candidates else None
|
|
126
|
+
|
|
127
|
+
async def select_agents(
|
|
128
|
+
self,
|
|
129
|
+
task: TaskDescription,
|
|
130
|
+
max_candidates: int = 3,
|
|
131
|
+
) -> list[AgentScore]:
|
|
132
|
+
"""Select the best agents for a task, ordered by score."""
|
|
133
|
+
registry = await self.get_registry()
|
|
134
|
+
|
|
135
|
+
# Analyze task to determine required capabilities
|
|
136
|
+
required_capabilities = self._analyze_task_capabilities(task)
|
|
137
|
+
|
|
138
|
+
# Score all agents
|
|
139
|
+
scores: list[AgentScore] = []
|
|
140
|
+
|
|
141
|
+
for agent in registry.list_all_agents():
|
|
142
|
+
score = await self._score_agent_for_task(agent, task, required_capabilities)
|
|
143
|
+
if score.final_score > 0.1: # Minimum threshold
|
|
144
|
+
scores.append(score)
|
|
145
|
+
|
|
146
|
+
# Sort by final score (descending)
|
|
147
|
+
scores.sort(key=lambda s: s.final_score, reverse=True)
|
|
148
|
+
|
|
149
|
+
# Return top candidates
|
|
150
|
+
selected = scores[:max_candidates]
|
|
151
|
+
|
|
152
|
+
self.logger.debug(
|
|
153
|
+
f"Selected {len(selected)} agents for task '{task.description[:50]}...': "
|
|
154
|
+
f"{[f'{s.agent.metadata.name}({s.final_score:.2f})' for s in selected]}"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
return selected
|
|
158
|
+
|
|
159
|
+
def _analyze_task_capabilities(self, task: TaskDescription) -> set[AgentCapability]:
|
|
160
|
+
"""Analyze a task to determine required capabilities."""
|
|
161
|
+
capabilities = set()
|
|
162
|
+
|
|
163
|
+
# Apply different analysis methods
|
|
164
|
+
capabilities.update(self._analyze_text_patterns(task))
|
|
165
|
+
capabilities.update(self._analyze_context(task))
|
|
166
|
+
capabilities.update(self._analyze_file_patterns(task))
|
|
167
|
+
capabilities.update(self._analyze_error_types(task))
|
|
168
|
+
|
|
169
|
+
# Fallback
|
|
170
|
+
return capabilities or {AgentCapability.CODE_ANALYSIS}
|
|
171
|
+
|
|
172
|
+
def _analyze_text_patterns(self, task: TaskDescription) -> set[AgentCapability]:
|
|
173
|
+
"""Analyze text patterns in task description and keywords."""
|
|
174
|
+
text = task.description.lower()
|
|
175
|
+
if task.keywords:
|
|
176
|
+
text += " " + " ".join(task.keywords).lower()
|
|
177
|
+
|
|
178
|
+
capabilities = set()
|
|
179
|
+
for pattern, caps in self._task_patterns.items():
|
|
180
|
+
if re.search(pattern, text, re.IGNORECASE):
|
|
181
|
+
capabilities.update(caps)
|
|
182
|
+
|
|
183
|
+
return capabilities
|
|
184
|
+
|
|
185
|
+
def _analyze_context(self, task: TaskDescription) -> set[AgentCapability]:
|
|
186
|
+
"""Analyze task context to determine capabilities."""
|
|
187
|
+
if not task.context:
|
|
188
|
+
return set()
|
|
189
|
+
|
|
190
|
+
context_map = {
|
|
191
|
+
TaskContext.CODE_QUALITY: [
|
|
192
|
+
AgentCapability.CODE_ANALYSIS,
|
|
193
|
+
AgentCapability.FORMATTING,
|
|
194
|
+
],
|
|
195
|
+
TaskContext.ARCHITECTURE: [
|
|
196
|
+
AgentCapability.ARCHITECTURE,
|
|
197
|
+
AgentCapability.CODE_ANALYSIS,
|
|
198
|
+
],
|
|
199
|
+
TaskContext.TESTING: [AgentCapability.TESTING],
|
|
200
|
+
TaskContext.REFACTORING: [
|
|
201
|
+
AgentCapability.REFACTORING,
|
|
202
|
+
AgentCapability.CODE_ANALYSIS,
|
|
203
|
+
],
|
|
204
|
+
TaskContext.DOCUMENTATION: [AgentCapability.DOCUMENTATION],
|
|
205
|
+
TaskContext.SECURITY: [AgentCapability.SECURITY],
|
|
206
|
+
TaskContext.PERFORMANCE: [
|
|
207
|
+
AgentCapability.PERFORMANCE,
|
|
208
|
+
AgentCapability.CODE_ANALYSIS,
|
|
209
|
+
],
|
|
210
|
+
TaskContext.DEBUGGING: [
|
|
211
|
+
AgentCapability.DEBUGGING,
|
|
212
|
+
AgentCapability.CODE_ANALYSIS,
|
|
213
|
+
],
|
|
214
|
+
TaskContext.PROJECT_SETUP: [AgentCapability.PROJECT_MANAGEMENT],
|
|
215
|
+
TaskContext.GENERAL: [AgentCapability.CODE_ANALYSIS],
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return set(context_map.get(task.context, []))
|
|
219
|
+
|
|
220
|
+
def _analyze_file_patterns(self, task: TaskDescription) -> set[AgentCapability]:
|
|
221
|
+
"""Analyze file patterns to determine capabilities."""
|
|
222
|
+
if not task.file_patterns:
|
|
223
|
+
return set()
|
|
224
|
+
|
|
225
|
+
capabilities = set()
|
|
226
|
+
for pattern in task.file_patterns:
|
|
227
|
+
pattern_lower = pattern.lower()
|
|
228
|
+
if any(ext in pattern_lower for ext in (".py", ".pyi")):
|
|
229
|
+
capabilities.add(AgentCapability.CODE_ANALYSIS)
|
|
230
|
+
if any(test in pattern_lower for test in ("test_", "_test", "tests/")):
|
|
231
|
+
capabilities.add(AgentCapability.TESTING)
|
|
232
|
+
|
|
233
|
+
return capabilities
|
|
234
|
+
|
|
235
|
+
def _analyze_error_types(self, task: TaskDescription) -> set[AgentCapability]:
|
|
236
|
+
"""Analyze error types to determine capabilities."""
|
|
237
|
+
if not task.error_types:
|
|
238
|
+
return set()
|
|
239
|
+
|
|
240
|
+
capabilities = set()
|
|
241
|
+
for error_type in task.error_types:
|
|
242
|
+
error_lower = error_type.lower()
|
|
243
|
+
if "furb" in error_lower or "refurb" in error_lower:
|
|
244
|
+
capabilities.update(
|
|
245
|
+
[AgentCapability.REFACTORING, AgentCapability.CODE_ANALYSIS]
|
|
246
|
+
)
|
|
247
|
+
elif "test" in error_lower:
|
|
248
|
+
capabilities.add(AgentCapability.TESTING)
|
|
249
|
+
elif "type" in error_lower or "pyright" in error_lower:
|
|
250
|
+
capabilities.add(AgentCapability.CODE_ANALYSIS)
|
|
251
|
+
elif "security" in error_lower or "bandit" in error_lower:
|
|
252
|
+
capabilities.add(AgentCapability.SECURITY)
|
|
253
|
+
|
|
254
|
+
return capabilities
|
|
255
|
+
|
|
256
|
+
async def _score_agent_for_task(
|
|
257
|
+
self,
|
|
258
|
+
agent: RegisteredAgent,
|
|
259
|
+
task: TaskDescription,
|
|
260
|
+
required_capabilities: set[AgentCapability],
|
|
261
|
+
) -> AgentScore:
|
|
262
|
+
"""Score an agent's suitability for a task."""
|
|
263
|
+
# Base score: capability overlap
|
|
264
|
+
agent_capabilities = agent.metadata.capabilities
|
|
265
|
+
overlap = len(required_capabilities & agent_capabilities)
|
|
266
|
+
max_overlap = len(required_capabilities)
|
|
267
|
+
|
|
268
|
+
base_score = overlap / max_overlap if max_overlap > 0 else 0.0
|
|
269
|
+
|
|
270
|
+
# Context score: how well the agent matches the task context
|
|
271
|
+
context_score = self._calculate_context_score(agent, task)
|
|
272
|
+
|
|
273
|
+
# Priority bonus: normalized agent priority (0-1)
|
|
274
|
+
priority_bonus = min(agent.metadata.priority / 100.0, 1.0)
|
|
275
|
+
|
|
276
|
+
# Confidence factor from agent metadata
|
|
277
|
+
confidence_factor = agent.metadata.confidence_factor
|
|
278
|
+
|
|
279
|
+
# Calculate weighted final score
|
|
280
|
+
weights = {
|
|
281
|
+
"base": 0.4, # Capability match is most important
|
|
282
|
+
"context": 0.3, # Context matching
|
|
283
|
+
"priority": 0.2, # Agent priority (source-based)
|
|
284
|
+
"bonus": 0.1, # Additional considerations
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
weighted_score = (
|
|
288
|
+
base_score * weights["base"]
|
|
289
|
+
+ context_score * weights["context"]
|
|
290
|
+
+ priority_bonus * weights["priority"]
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Apply confidence factor
|
|
294
|
+
final_score = weighted_score * confidence_factor
|
|
295
|
+
|
|
296
|
+
# Generate reasoning
|
|
297
|
+
reasoning = self._generate_score_reasoning(
|
|
298
|
+
agent, base_score, context_score, priority_bonus, required_capabilities
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
return AgentScore(
|
|
302
|
+
agent=agent,
|
|
303
|
+
base_score=base_score,
|
|
304
|
+
context_score=context_score,
|
|
305
|
+
priority_bonus=priority_bonus,
|
|
306
|
+
confidence_factor=confidence_factor,
|
|
307
|
+
final_score=final_score,
|
|
308
|
+
reasoning=reasoning,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
def _calculate_context_score(
|
|
312
|
+
self, agent: RegisteredAgent, task: TaskDescription
|
|
313
|
+
) -> float:
|
|
314
|
+
"""Calculate how well an agent matches the task context."""
|
|
315
|
+
score = 0.0
|
|
316
|
+
|
|
317
|
+
agent_name_lower = agent.metadata.name.lower()
|
|
318
|
+
task_text = task.description.lower()
|
|
319
|
+
|
|
320
|
+
score += self._score_name_matches(agent_name_lower, task_text)
|
|
321
|
+
score += self._score_description_matches(agent, task_text)
|
|
322
|
+
score += self._score_keyword_matches(agent, task)
|
|
323
|
+
score += self._score_special_patterns(agent_name_lower, task_text)
|
|
324
|
+
|
|
325
|
+
return min(score, 1.0)
|
|
326
|
+
|
|
327
|
+
def _score_name_matches(self, agent_name_lower: str, task_text: str) -> float:
|
|
328
|
+
"""Score direct name matches between agent and task."""
|
|
329
|
+
if any(keyword in agent_name_lower for keyword in task_text.split()):
|
|
330
|
+
return 0.3
|
|
331
|
+
return 0.0
|
|
332
|
+
|
|
333
|
+
def _score_description_matches(
|
|
334
|
+
self, agent: RegisteredAgent, task_text: str
|
|
335
|
+
) -> float:
|
|
336
|
+
"""Score description word overlap."""
|
|
337
|
+
if not agent.metadata.description:
|
|
338
|
+
return 0.0
|
|
339
|
+
|
|
340
|
+
desc_words = set(agent.metadata.description.lower().split())
|
|
341
|
+
task_words = set(task_text.split())
|
|
342
|
+
common_words = desc_words & task_words
|
|
343
|
+
|
|
344
|
+
if common_words:
|
|
345
|
+
return len(common_words) / max(len(task_words), 1) * 0.2
|
|
346
|
+
return 0.0
|
|
347
|
+
|
|
348
|
+
def _score_keyword_matches(
|
|
349
|
+
self, agent: RegisteredAgent, task: TaskDescription
|
|
350
|
+
) -> float:
|
|
351
|
+
"""Score keyword/tag overlap."""
|
|
352
|
+
if not task.keywords or not agent.metadata.tags:
|
|
353
|
+
return 0.0
|
|
354
|
+
|
|
355
|
+
task_keywords = {k.lower() for k in task.keywords}
|
|
356
|
+
agent_tags = {t.lower() for t in agent.metadata.tags}
|
|
357
|
+
overlap = len(task_keywords & agent_tags)
|
|
358
|
+
|
|
359
|
+
if overlap > 0:
|
|
360
|
+
return overlap / len(task_keywords) * 0.3
|
|
361
|
+
return 0.0
|
|
362
|
+
|
|
363
|
+
def _score_special_patterns(self, agent_name_lower: str, task_text: str) -> float:
|
|
364
|
+
"""Score special pattern bonuses."""
|
|
365
|
+
score = 0.0
|
|
366
|
+
|
|
367
|
+
if "architect" in agent_name_lower and (
|
|
368
|
+
"architect" in task_text or "design" in task_text
|
|
369
|
+
):
|
|
370
|
+
score += 0.2
|
|
371
|
+
if "refactor" in agent_name_lower and "refurb" in task_text:
|
|
372
|
+
score += 0.2
|
|
373
|
+
if "test" in agent_name_lower and "test" in task_text:
|
|
374
|
+
score += 0.2
|
|
375
|
+
|
|
376
|
+
return score
|
|
377
|
+
|
|
378
|
+
def _generate_score_reasoning(
|
|
379
|
+
self,
|
|
380
|
+
agent: RegisteredAgent,
|
|
381
|
+
base_score: float,
|
|
382
|
+
context_score: float,
|
|
383
|
+
priority_bonus: float,
|
|
384
|
+
required_capabilities: set[AgentCapability],
|
|
385
|
+
) -> str:
|
|
386
|
+
"""Generate human-readable reasoning for the score."""
|
|
387
|
+
parts = []
|
|
388
|
+
|
|
389
|
+
# Capability match
|
|
390
|
+
overlap = len(required_capabilities & agent.metadata.capabilities)
|
|
391
|
+
parts.append(f"Capabilities: {overlap}/{len(required_capabilities)} match")
|
|
392
|
+
|
|
393
|
+
# Context relevance
|
|
394
|
+
if context_score > 0.5:
|
|
395
|
+
parts.append("High context relevance")
|
|
396
|
+
elif context_score > 0.2:
|
|
397
|
+
parts.append("Moderate context relevance")
|
|
398
|
+
else:
|
|
399
|
+
parts.append("Low context relevance")
|
|
400
|
+
|
|
401
|
+
# Source priority
|
|
402
|
+
source_desc = {
|
|
403
|
+
"crackerjack": "Built-in specialist",
|
|
404
|
+
"user": "User agent",
|
|
405
|
+
"system": "System agent",
|
|
406
|
+
}
|
|
407
|
+
parts.append(source_desc.get(agent.metadata.source.value, "Unknown source"))
|
|
408
|
+
|
|
409
|
+
# Special strengths
|
|
410
|
+
if agent.metadata.capabilities:
|
|
411
|
+
top_caps = list(agent.metadata.capabilities)[:2]
|
|
412
|
+
cap_names = [cap.value.replace("_", " ") for cap in top_caps]
|
|
413
|
+
parts.append(f"Strengths: {', '.join(cap_names)}")
|
|
414
|
+
|
|
415
|
+
return " | ".join(parts)
|
|
416
|
+
|
|
417
|
+
async def analyze_task_complexity(self, task: TaskDescription) -> dict[str, t.Any]:
|
|
418
|
+
"""Analyze task complexity and provide recommendations."""
|
|
419
|
+
registry = await self.get_registry()
|
|
420
|
+
required_capabilities = self._analyze_task_capabilities(task)
|
|
421
|
+
|
|
422
|
+
# Get all agents that could handle this task
|
|
423
|
+
all_scores = []
|
|
424
|
+
for agent in registry.list_all_agents():
|
|
425
|
+
score = await self._score_agent_for_task(agent, task, required_capabilities)
|
|
426
|
+
if score.final_score > 0.1:
|
|
427
|
+
all_scores.append(score)
|
|
428
|
+
|
|
429
|
+
all_scores.sort(key=lambda s: s.final_score, reverse=True)
|
|
430
|
+
|
|
431
|
+
analysis = {
|
|
432
|
+
"required_capabilities": [cap.value for cap in required_capabilities],
|
|
433
|
+
"complexity_level": self._assess_complexity(
|
|
434
|
+
required_capabilities, all_scores
|
|
435
|
+
),
|
|
436
|
+
"candidate_count": len(all_scores),
|
|
437
|
+
"top_agents": [
|
|
438
|
+
{
|
|
439
|
+
"name": score.agent.metadata.name,
|
|
440
|
+
"source": score.agent.metadata.source.value,
|
|
441
|
+
"score": score.final_score,
|
|
442
|
+
"reasoning": score.reasoning,
|
|
443
|
+
}
|
|
444
|
+
for score in all_scores[:5]
|
|
445
|
+
],
|
|
446
|
+
"recommendations": self._generate_recommendations(
|
|
447
|
+
required_capabilities, all_scores
|
|
448
|
+
),
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return analysis
|
|
452
|
+
|
|
453
|
+
def _assess_complexity(
|
|
454
|
+
self, capabilities: set[AgentCapability], scores: list[AgentScore]
|
|
455
|
+
) -> str:
|
|
456
|
+
"""Assess the complexity level of a task."""
|
|
457
|
+
if len(capabilities) >= 4:
|
|
458
|
+
return "high"
|
|
459
|
+
elif len(capabilities) >= 2:
|
|
460
|
+
return "medium"
|
|
461
|
+
elif not scores or scores[0].final_score < 0.3:
|
|
462
|
+
return "high" # No good matches = complex
|
|
463
|
+
|
|
464
|
+
return "low"
|
|
465
|
+
|
|
466
|
+
def _generate_recommendations(
|
|
467
|
+
self,
|
|
468
|
+
capabilities: set[AgentCapability],
|
|
469
|
+
scores: list[AgentScore],
|
|
470
|
+
) -> list[str]:
|
|
471
|
+
"""Generate recommendations based on analysis."""
|
|
472
|
+
recommendations = []
|
|
473
|
+
|
|
474
|
+
if not scores:
|
|
475
|
+
recommendations.append(
|
|
476
|
+
"No suitable agents found - consider manual approach"
|
|
477
|
+
)
|
|
478
|
+
return recommendations
|
|
479
|
+
|
|
480
|
+
top_score = scores[0].final_score
|
|
481
|
+
|
|
482
|
+
if top_score > 0.8:
|
|
483
|
+
recommendations.append(
|
|
484
|
+
"Excellent agent match found - high confidence execution"
|
|
485
|
+
)
|
|
486
|
+
elif top_score > 0.6:
|
|
487
|
+
recommendations.append("Good agent match - should handle task well")
|
|
488
|
+
elif top_score > 0.4:
|
|
489
|
+
recommendations.append("Moderate match - may need supervision")
|
|
490
|
+
else:
|
|
491
|
+
recommendations.append("Weak matches - consider alternative approaches")
|
|
492
|
+
|
|
493
|
+
# Multi-agent recommendations
|
|
494
|
+
if len(capabilities) > 2:
|
|
495
|
+
recommendations.append("Consider multi-agent approach for complex task")
|
|
496
|
+
|
|
497
|
+
# Source diversity
|
|
498
|
+
sources = {score.agent.metadata.source for score in scores[:3]}
|
|
499
|
+
if len(sources) > 1:
|
|
500
|
+
recommendations.append("Multiple agent sources available for redundancy")
|
|
501
|
+
|
|
502
|
+
return recommendations
|