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
|
-
"""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
1
|
import logging
|
|
8
2
|
import re
|
|
9
3
|
import typing as t
|
|
@@ -19,8 +13,6 @@ from .agent_registry import (
|
|
|
19
13
|
|
|
20
14
|
|
|
21
15
|
class TaskContext(Enum):
|
|
22
|
-
"""Context categories for tasks."""
|
|
23
|
-
|
|
24
16
|
CODE_QUALITY = "code_quality"
|
|
25
17
|
ARCHITECTURE = "architecture"
|
|
26
18
|
TESTING = "testing"
|
|
@@ -35,32 +27,26 @@ class TaskContext(Enum):
|
|
|
35
27
|
|
|
36
28
|
@dataclass
|
|
37
29
|
class TaskDescription:
|
|
38
|
-
"""Description of a task to be executed."""
|
|
39
|
-
|
|
40
30
|
description: str
|
|
41
31
|
context: TaskContext | None = None
|
|
42
32
|
keywords: list[str] | None = None
|
|
43
33
|
file_patterns: list[str] | None = None
|
|
44
34
|
error_types: list[str] | None = None
|
|
45
|
-
priority: int = 50
|
|
35
|
+
priority: int = 50
|
|
46
36
|
|
|
47
37
|
|
|
48
38
|
@dataclass
|
|
49
39
|
class AgentScore:
|
|
50
|
-
"""Score for an agent's suitability for a task."""
|
|
51
|
-
|
|
52
40
|
agent: RegisteredAgent
|
|
53
|
-
base_score: float
|
|
54
|
-
context_score: float
|
|
55
|
-
priority_bonus: float
|
|
56
|
-
confidence_factor: float
|
|
57
|
-
final_score: float
|
|
58
|
-
reasoning: str
|
|
41
|
+
base_score: float
|
|
42
|
+
context_score: float
|
|
43
|
+
priority_bonus: float
|
|
44
|
+
confidence_factor: float
|
|
45
|
+
final_score: float
|
|
46
|
+
reasoning: str
|
|
59
47
|
|
|
60
48
|
|
|
61
49
|
class AgentSelector:
|
|
62
|
-
"""Intelligent agent selection engine."""
|
|
63
|
-
|
|
64
50
|
def __init__(self, registry: AgentRegistry | None = None) -> None:
|
|
65
51
|
self.logger = logging.getLogger(__name__)
|
|
66
52
|
self.registry = registry
|
|
@@ -68,49 +54,39 @@ class AgentSelector:
|
|
|
68
54
|
self._initialize_task_patterns()
|
|
69
55
|
|
|
70
56
|
def _initialize_task_patterns(self) -> None:
|
|
71
|
-
"""Initialize patterns for task analysis."""
|
|
72
57
|
self._task_patterns = {
|
|
73
|
-
|
|
74
|
-
r"architect|design|structure|pattern|refactor.*complex": [
|
|
58
|
+
r"architect | design | structure | pattern | refactor.* complex": [
|
|
75
59
|
AgentCapability.ARCHITECTURE,
|
|
76
60
|
AgentCapability.REFACTORING,
|
|
77
61
|
],
|
|
78
|
-
|
|
79
|
-
r"refurb|ruff|format|lint|style|clean.*code": [
|
|
62
|
+
r"refurb | ruff | format | lint | style | clean.* code": [
|
|
80
63
|
AgentCapability.FORMATTING,
|
|
81
64
|
AgentCapability.CODE_ANALYSIS,
|
|
82
65
|
],
|
|
83
|
-
|
|
84
|
-
r"test|pytest|coverage|mock|fixture": [
|
|
66
|
+
r"test | pytest | coverage | mock | fixture": [
|
|
85
67
|
AgentCapability.TESTING,
|
|
86
68
|
],
|
|
87
|
-
|
|
88
|
-
r"security|vulnerability|audit|bandit|safe": [
|
|
69
|
+
r"security | vulnerability | audit | bandit | safe": [
|
|
89
70
|
AgentCapability.SECURITY,
|
|
90
71
|
],
|
|
91
|
-
|
|
92
|
-
r"performance|optimize|speed|efficient|complexity": [
|
|
72
|
+
r"performance | optimize | speed | efficient | complexity": [
|
|
93
73
|
AgentCapability.PERFORMANCE,
|
|
94
74
|
AgentCapability.CODE_ANALYSIS,
|
|
95
75
|
],
|
|
96
|
-
|
|
97
|
-
r"document|readme|comment|explain|changelog": [
|
|
76
|
+
r"document | readme | comment | explain | changelog": [
|
|
98
77
|
AgentCapability.DOCUMENTATION,
|
|
99
78
|
],
|
|
100
|
-
|
|
101
|
-
r"refactor|improve|simplify|dry.*violation": [
|
|
79
|
+
r"refactor | improve | simplify | dry.* violation": [
|
|
102
80
|
AgentCapability.REFACTORING,
|
|
103
81
|
AgentCapability.CODE_ANALYSIS,
|
|
104
82
|
],
|
|
105
|
-
|
|
106
|
-
r"debug|fix|error|bug|failure": [
|
|
83
|
+
r"debug | fix | error | bug | failure": [
|
|
107
84
|
AgentCapability.DEBUGGING,
|
|
108
85
|
AgentCapability.CODE_ANALYSIS,
|
|
109
86
|
],
|
|
110
87
|
}
|
|
111
88
|
|
|
112
89
|
async def get_registry(self) -> AgentRegistry:
|
|
113
|
-
"""Get or initialize the agent registry."""
|
|
114
90
|
if self.registry is None:
|
|
115
91
|
self.registry = await get_agent_registry()
|
|
116
92
|
return self.registry
|
|
@@ -120,7 +96,6 @@ class AgentSelector:
|
|
|
120
96
|
task: TaskDescription,
|
|
121
97
|
max_candidates: int = 5,
|
|
122
98
|
) -> AgentScore | None:
|
|
123
|
-
"""Select the single best agent for a task."""
|
|
124
99
|
candidates = await self.select_agents(task, max_candidates)
|
|
125
100
|
return candidates[0] if candidates else None
|
|
126
101
|
|
|
@@ -129,61 +104,53 @@ class AgentSelector:
|
|
|
129
104
|
task: TaskDescription,
|
|
130
105
|
max_candidates: int = 3,
|
|
131
106
|
) -> list[AgentScore]:
|
|
132
|
-
"""Select the best agents for a task, ordered by score."""
|
|
133
107
|
registry = await self.get_registry()
|
|
134
108
|
|
|
135
|
-
# Analyze task to determine required capabilities
|
|
136
109
|
required_capabilities = self._analyze_task_capabilities(task)
|
|
137
110
|
|
|
138
|
-
# Score all agents
|
|
139
111
|
scores: list[AgentScore] = []
|
|
140
112
|
|
|
141
113
|
for agent in registry.list_all_agents():
|
|
142
114
|
score = await self._score_agent_for_task(agent, task, required_capabilities)
|
|
143
|
-
if score.final_score > 0.1:
|
|
115
|
+
if score.final_score > 0.1:
|
|
144
116
|
scores.append(score)
|
|
145
117
|
|
|
146
|
-
# Sort by final score (descending)
|
|
147
118
|
scores.sort(key=lambda s: s.final_score, reverse=True)
|
|
148
119
|
|
|
149
|
-
# Return top candidates
|
|
150
120
|
selected = scores[:max_candidates]
|
|
151
121
|
|
|
152
122
|
self.logger.debug(
|
|
153
123
|
f"Selected {len(selected)} agents for task '{task.description[:50]}...': "
|
|
154
|
-
f"{[f'{s.agent.metadata.name}({s.final_score
|
|
124
|
+
f"{[f'{s.agent.metadata.name}({s.final_score: .2f})' for s in selected]}"
|
|
155
125
|
)
|
|
156
126
|
|
|
157
127
|
return selected
|
|
158
128
|
|
|
159
129
|
def _analyze_task_capabilities(self, task: TaskDescription) -> set[AgentCapability]:
|
|
160
|
-
"""Analyze a task to determine required capabilities."""
|
|
161
130
|
capabilities = set()
|
|
162
131
|
|
|
163
|
-
# Apply different analysis methods
|
|
164
132
|
capabilities.update(self._analyze_text_patterns(task))
|
|
165
133
|
capabilities.update(self._analyze_context(task))
|
|
166
134
|
capabilities.update(self._analyze_file_patterns(task))
|
|
167
135
|
capabilities.update(self._analyze_error_types(task))
|
|
168
136
|
|
|
169
|
-
# Fallback
|
|
170
137
|
return capabilities or {AgentCapability.CODE_ANALYSIS}
|
|
171
138
|
|
|
172
139
|
def _analyze_text_patterns(self, task: TaskDescription) -> set[AgentCapability]:
|
|
173
|
-
"""Analyze text patterns in task description and keywords."""
|
|
174
140
|
text = task.description.lower()
|
|
175
141
|
if task.keywords:
|
|
176
142
|
text += " " + " ".join(task.keywords).lower()
|
|
177
143
|
|
|
178
144
|
capabilities = set()
|
|
179
145
|
for pattern, caps in self._task_patterns.items():
|
|
180
|
-
if re.search(
|
|
146
|
+
if re.search(
|
|
147
|
+
pattern, text, re.IGNORECASE
|
|
148
|
+
): # REGEX OK: dynamic pattern matching from config
|
|
181
149
|
capabilities.update(caps)
|
|
182
150
|
|
|
183
151
|
return capabilities
|
|
184
152
|
|
|
185
153
|
def _analyze_context(self, task: TaskDescription) -> set[AgentCapability]:
|
|
186
|
-
"""Analyze task context to determine capabilities."""
|
|
187
154
|
if not task.context:
|
|
188
155
|
return set()
|
|
189
156
|
|
|
@@ -218,7 +185,6 @@ class AgentSelector:
|
|
|
218
185
|
return set(context_map.get(task.context, []))
|
|
219
186
|
|
|
220
187
|
def _analyze_file_patterns(self, task: TaskDescription) -> set[AgentCapability]:
|
|
221
|
-
"""Analyze file patterns to determine capabilities."""
|
|
222
188
|
if not task.file_patterns:
|
|
223
189
|
return set()
|
|
224
190
|
|
|
@@ -227,13 +193,12 @@ class AgentSelector:
|
|
|
227
193
|
pattern_lower = pattern.lower()
|
|
228
194
|
if any(ext in pattern_lower for ext in (".py", ".pyi")):
|
|
229
195
|
capabilities.add(AgentCapability.CODE_ANALYSIS)
|
|
230
|
-
if any(test in pattern_lower for test in ("test_", "_test", "tests/")):
|
|
196
|
+
if any(test in pattern_lower for test in ("test_", "_test", "tests /")):
|
|
231
197
|
capabilities.add(AgentCapability.TESTING)
|
|
232
198
|
|
|
233
199
|
return capabilities
|
|
234
200
|
|
|
235
201
|
def _analyze_error_types(self, task: TaskDescription) -> set[AgentCapability]:
|
|
236
|
-
"""Analyze error types to determine capabilities."""
|
|
237
202
|
if not task.error_types:
|
|
238
203
|
return set()
|
|
239
204
|
|
|
@@ -259,29 +224,23 @@ class AgentSelector:
|
|
|
259
224
|
task: TaskDescription,
|
|
260
225
|
required_capabilities: set[AgentCapability],
|
|
261
226
|
) -> AgentScore:
|
|
262
|
-
"""Score an agent's suitability for a task."""
|
|
263
|
-
# Base score: capability overlap
|
|
264
227
|
agent_capabilities = agent.metadata.capabilities
|
|
265
228
|
overlap = len(required_capabilities & agent_capabilities)
|
|
266
229
|
max_overlap = len(required_capabilities)
|
|
267
230
|
|
|
268
231
|
base_score = overlap / max_overlap if max_overlap > 0 else 0.0
|
|
269
232
|
|
|
270
|
-
# Context score: how well the agent matches the task context
|
|
271
233
|
context_score = self._calculate_context_score(agent, task)
|
|
272
234
|
|
|
273
|
-
# Priority bonus: normalized agent priority (0-1)
|
|
274
235
|
priority_bonus = min(agent.metadata.priority / 100.0, 1.0)
|
|
275
236
|
|
|
276
|
-
# Confidence factor from agent metadata
|
|
277
237
|
confidence_factor = agent.metadata.confidence_factor
|
|
278
238
|
|
|
279
|
-
# Calculate weighted final score
|
|
280
239
|
weights = {
|
|
281
|
-
"base": 0.4,
|
|
282
|
-
"context": 0.3,
|
|
283
|
-
"priority": 0.2,
|
|
284
|
-
"bonus": 0.1,
|
|
240
|
+
"base": 0.4,
|
|
241
|
+
"context": 0.3,
|
|
242
|
+
"priority": 0.2,
|
|
243
|
+
"bonus": 0.1,
|
|
285
244
|
}
|
|
286
245
|
|
|
287
246
|
weighted_score = (
|
|
@@ -290,10 +249,8 @@ class AgentSelector:
|
|
|
290
249
|
+ priority_bonus * weights["priority"]
|
|
291
250
|
)
|
|
292
251
|
|
|
293
|
-
# Apply confidence factor
|
|
294
252
|
final_score = weighted_score * confidence_factor
|
|
295
253
|
|
|
296
|
-
# Generate reasoning
|
|
297
254
|
reasoning = self._generate_score_reasoning(
|
|
298
255
|
agent, base_score, context_score, priority_bonus, required_capabilities
|
|
299
256
|
)
|
|
@@ -311,7 +268,6 @@ class AgentSelector:
|
|
|
311
268
|
def _calculate_context_score(
|
|
312
269
|
self, agent: RegisteredAgent, task: TaskDescription
|
|
313
270
|
) -> float:
|
|
314
|
-
"""Calculate how well an agent matches the task context."""
|
|
315
271
|
score = 0.0
|
|
316
272
|
|
|
317
273
|
agent_name_lower = agent.metadata.name.lower()
|
|
@@ -325,7 +281,6 @@ class AgentSelector:
|
|
|
325
281
|
return min(score, 1.0)
|
|
326
282
|
|
|
327
283
|
def _score_name_matches(self, agent_name_lower: str, task_text: str) -> float:
|
|
328
|
-
"""Score direct name matches between agent and task."""
|
|
329
284
|
if any(keyword in agent_name_lower for keyword in task_text.split()):
|
|
330
285
|
return 0.3
|
|
331
286
|
return 0.0
|
|
@@ -333,7 +288,6 @@ class AgentSelector:
|
|
|
333
288
|
def _score_description_matches(
|
|
334
289
|
self, agent: RegisteredAgent, task_text: str
|
|
335
290
|
) -> float:
|
|
336
|
-
"""Score description word overlap."""
|
|
337
291
|
if not agent.metadata.description:
|
|
338
292
|
return 0.0
|
|
339
293
|
|
|
@@ -348,7 +302,6 @@ class AgentSelector:
|
|
|
348
302
|
def _score_keyword_matches(
|
|
349
303
|
self, agent: RegisteredAgent, task: TaskDescription
|
|
350
304
|
) -> float:
|
|
351
|
-
"""Score keyword/tag overlap."""
|
|
352
305
|
if not task.keywords or not agent.metadata.tags:
|
|
353
306
|
return 0.0
|
|
354
307
|
|
|
@@ -361,7 +314,6 @@ class AgentSelector:
|
|
|
361
314
|
return 0.0
|
|
362
315
|
|
|
363
316
|
def _score_special_patterns(self, agent_name_lower: str, task_text: str) -> float:
|
|
364
|
-
"""Score special pattern bonuses."""
|
|
365
317
|
score = 0.0
|
|
366
318
|
|
|
367
319
|
if "architect" in agent_name_lower and (
|
|
@@ -383,14 +335,11 @@ class AgentSelector:
|
|
|
383
335
|
priority_bonus: float,
|
|
384
336
|
required_capabilities: set[AgentCapability],
|
|
385
337
|
) -> str:
|
|
386
|
-
"""Generate human-readable reasoning for the score."""
|
|
387
338
|
parts = []
|
|
388
339
|
|
|
389
|
-
# Capability match
|
|
390
340
|
overlap = len(required_capabilities & agent.metadata.capabilities)
|
|
391
341
|
parts.append(f"Capabilities: {overlap}/{len(required_capabilities)} match")
|
|
392
342
|
|
|
393
|
-
# Context relevance
|
|
394
343
|
if context_score > 0.5:
|
|
395
344
|
parts.append("High context relevance")
|
|
396
345
|
elif context_score > 0.2:
|
|
@@ -398,7 +347,6 @@ class AgentSelector:
|
|
|
398
347
|
else:
|
|
399
348
|
parts.append("Low context relevance")
|
|
400
349
|
|
|
401
|
-
# Source priority
|
|
402
350
|
source_desc = {
|
|
403
351
|
"crackerjack": "Built-in specialist",
|
|
404
352
|
"user": "User agent",
|
|
@@ -406,7 +354,6 @@ class AgentSelector:
|
|
|
406
354
|
}
|
|
407
355
|
parts.append(source_desc.get(agent.metadata.source.value, "Unknown source"))
|
|
408
356
|
|
|
409
|
-
# Special strengths
|
|
410
357
|
if agent.metadata.capabilities:
|
|
411
358
|
top_caps = list(agent.metadata.capabilities)[:2]
|
|
412
359
|
cap_names = [cap.value.replace("_", " ") for cap in top_caps]
|
|
@@ -415,11 +362,9 @@ class AgentSelector:
|
|
|
415
362
|
return " | ".join(parts)
|
|
416
363
|
|
|
417
364
|
async def analyze_task_complexity(self, task: TaskDescription) -> dict[str, t.Any]:
|
|
418
|
-
"""Analyze task complexity and provide recommendations."""
|
|
419
365
|
registry = await self.get_registry()
|
|
420
366
|
required_capabilities = self._analyze_task_capabilities(task)
|
|
421
367
|
|
|
422
|
-
# Get all agents that could handle this task
|
|
423
368
|
all_scores = []
|
|
424
369
|
for agent in registry.list_all_agents():
|
|
425
370
|
score = await self._score_agent_for_task(agent, task, required_capabilities)
|
|
@@ -453,13 +398,12 @@ class AgentSelector:
|
|
|
453
398
|
def _assess_complexity(
|
|
454
399
|
self, capabilities: set[AgentCapability], scores: list[AgentScore]
|
|
455
400
|
) -> str:
|
|
456
|
-
"""Assess the complexity level of a task."""
|
|
457
401
|
if len(capabilities) >= 4:
|
|
458
402
|
return "high"
|
|
459
403
|
elif len(capabilities) >= 2:
|
|
460
404
|
return "medium"
|
|
461
405
|
elif not scores or scores[0].final_score < 0.3:
|
|
462
|
-
return "high"
|
|
406
|
+
return "high"
|
|
463
407
|
|
|
464
408
|
return "low"
|
|
465
409
|
|
|
@@ -468,33 +412,28 @@ class AgentSelector:
|
|
|
468
412
|
capabilities: set[AgentCapability],
|
|
469
413
|
scores: list[AgentScore],
|
|
470
414
|
) -> list[str]:
|
|
471
|
-
"""Generate recommendations based on analysis."""
|
|
472
415
|
recommendations = []
|
|
473
416
|
|
|
474
417
|
if not scores:
|
|
475
|
-
recommendations.append(
|
|
476
|
-
"No suitable agents found - consider manual approach"
|
|
477
|
-
)
|
|
418
|
+
recommendations.append("No suitable agents found-consider manual approach")
|
|
478
419
|
return recommendations
|
|
479
420
|
|
|
480
421
|
top_score = scores[0].final_score
|
|
481
422
|
|
|
482
423
|
if top_score > 0.8:
|
|
483
424
|
recommendations.append(
|
|
484
|
-
"Excellent agent match found
|
|
425
|
+
"Excellent agent match found-high confidence execution"
|
|
485
426
|
)
|
|
486
427
|
elif top_score > 0.6:
|
|
487
|
-
recommendations.append("Good agent match
|
|
428
|
+
recommendations.append("Good agent match-should handle task well")
|
|
488
429
|
elif top_score > 0.4:
|
|
489
|
-
recommendations.append("Moderate match
|
|
430
|
+
recommendations.append("Moderate match-may need supervision")
|
|
490
431
|
else:
|
|
491
|
-
recommendations.append("Weak matches
|
|
432
|
+
recommendations.append("Weak matches-consider alternative approaches")
|
|
492
433
|
|
|
493
|
-
# Multi-agent recommendations
|
|
494
434
|
if len(capabilities) > 2:
|
|
495
435
|
recommendations.append("Consider multi-agent approach for complex task")
|
|
496
436
|
|
|
497
|
-
# Source diversity
|
|
498
437
|
sources = {score.agent.metadata.source for score in scores[:3]}
|
|
499
438
|
if len(sources) > 1:
|
|
500
439
|
recommendations.append("Multiple agent sources available for redundancy")
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"""Integration layer for Intelligent Agent Selection System.
|
|
2
|
-
|
|
3
|
-
Provides high-level API for integrating the intelligent agent system with
|
|
4
|
-
existing crackerjack workflows and MCP tools.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
1
|
import logging
|
|
8
2
|
import typing as t
|
|
9
3
|
from dataclasses import dataclass
|
|
@@ -22,8 +16,6 @@ from .agent_selector import TaskContext, TaskDescription
|
|
|
22
16
|
|
|
23
17
|
@dataclass
|
|
24
18
|
class SmartAgentResult:
|
|
25
|
-
"""Result from smart agent execution."""
|
|
26
|
-
|
|
27
19
|
success: bool
|
|
28
20
|
result: t.Any
|
|
29
21
|
agents_used: list[str]
|
|
@@ -34,27 +26,22 @@ class SmartAgentResult:
|
|
|
34
26
|
|
|
35
27
|
|
|
36
28
|
class IntelligentAgentSystem:
|
|
37
|
-
"""High-level interface to the intelligent agent system."""
|
|
38
|
-
|
|
39
29
|
def __init__(self) -> None:
|
|
40
30
|
self.logger = logging.getLogger(__name__)
|
|
41
31
|
self._initialized = False
|
|
42
32
|
|
|
43
33
|
async def initialize(self) -> None:
|
|
44
|
-
"""Initialize the intelligent agent system."""
|
|
45
34
|
if self._initialized:
|
|
46
35
|
return
|
|
47
36
|
|
|
48
37
|
self.logger.info("Initializing Intelligent Agent System")
|
|
49
38
|
|
|
50
|
-
# Initialize all components
|
|
51
39
|
self.registry = await get_agent_registry()
|
|
52
40
|
self.orchestrator = await get_agent_orchestrator()
|
|
53
41
|
self.learning_system = await get_learning_system()
|
|
54
42
|
|
|
55
43
|
self._initialized = True
|
|
56
44
|
|
|
57
|
-
# Log system status
|
|
58
45
|
stats = self.registry.get_agent_stats()
|
|
59
46
|
self.logger.info(
|
|
60
47
|
f"System initialized: {stats['total_agents']} agents available "
|
|
@@ -68,16 +55,13 @@ class IntelligentAgentSystem:
|
|
|
68
55
|
strategy: ExecutionStrategy = ExecutionStrategy.SINGLE_BEST,
|
|
69
56
|
context_data: AgentContext | None = None,
|
|
70
57
|
) -> SmartAgentResult:
|
|
71
|
-
"""Execute a task using intelligent agent selection."""
|
|
72
58
|
await self.initialize()
|
|
73
59
|
|
|
74
|
-
# Create task description
|
|
75
60
|
task = TaskDescription(
|
|
76
61
|
description=description,
|
|
77
62
|
context=context,
|
|
78
63
|
)
|
|
79
64
|
|
|
80
|
-
# Get learning recommendations
|
|
81
65
|
candidates = await self.orchestrator.selector.select_agents(
|
|
82
66
|
task, max_candidates=5
|
|
83
67
|
)
|
|
@@ -87,19 +71,14 @@ class IntelligentAgentSystem:
|
|
|
87
71
|
task, candidate_names
|
|
88
72
|
)
|
|
89
73
|
|
|
90
|
-
# Apply learning to boost scores
|
|
91
74
|
for candidate in candidates:
|
|
92
75
|
agent_name = candidate.agent.metadata.name
|
|
93
76
|
if agent_name in learning_recommendations:
|
|
94
|
-
learning_boost =
|
|
95
|
-
learning_recommendations[agent_name] * 0.2
|
|
96
|
-
) # 20% boost max
|
|
77
|
+
learning_boost = learning_recommendations[agent_name] * 0.2
|
|
97
78
|
candidate.final_score = min(1.0, candidate.final_score + learning_boost)
|
|
98
79
|
|
|
99
|
-
# Re-sort by updated scores
|
|
100
80
|
candidates.sort(key=lambda c: c.final_score, reverse=True)
|
|
101
81
|
|
|
102
|
-
# Create execution request
|
|
103
82
|
request = ExecutionRequest(
|
|
104
83
|
task=task,
|
|
105
84
|
strategy=strategy,
|
|
@@ -107,10 +86,8 @@ class IntelligentAgentSystem:
|
|
|
107
86
|
context=context_data,
|
|
108
87
|
)
|
|
109
88
|
|
|
110
|
-
# Execute with orchestrator
|
|
111
89
|
result = await self.orchestrator.execute(request)
|
|
112
90
|
|
|
113
|
-
# Record results for learning
|
|
114
91
|
if candidates:
|
|
115
92
|
best_candidate = candidates[0]
|
|
116
93
|
await self.learning_system.record_execution(
|
|
@@ -122,7 +99,6 @@ class IntelligentAgentSystem:
|
|
|
122
99
|
error_message=result.error_message,
|
|
123
100
|
)
|
|
124
101
|
|
|
125
|
-
# Create smart result
|
|
126
102
|
return SmartAgentResult(
|
|
127
103
|
success=result.success,
|
|
128
104
|
result=result.primary_result,
|
|
@@ -139,10 +115,8 @@ class IntelligentAgentSystem:
|
|
|
139
115
|
context: AgentContext,
|
|
140
116
|
use_learning: bool = True,
|
|
141
117
|
) -> FixResult:
|
|
142
|
-
"""Handle a crackerjack Issue using intelligent agent selection."""
|
|
143
118
|
await self.initialize()
|
|
144
119
|
|
|
145
|
-
# Convert issue to task description
|
|
146
120
|
task_context = self._map_issue_to_task_context(issue)
|
|
147
121
|
|
|
148
122
|
task = TaskDescription(
|
|
@@ -152,18 +126,15 @@ class IntelligentAgentSystem:
|
|
|
152
126
|
priority=self._map_severity_to_priority(issue.severity),
|
|
153
127
|
)
|
|
154
128
|
|
|
155
|
-
# Execute smart task
|
|
156
129
|
smart_result = await self.execute_smart_task(
|
|
157
130
|
description=task.description,
|
|
158
131
|
context=task_context,
|
|
159
132
|
context_data=context,
|
|
160
133
|
)
|
|
161
134
|
|
|
162
|
-
# Convert result back to FixResult
|
|
163
135
|
if smart_result.success and isinstance(smart_result.result, FixResult):
|
|
164
136
|
return smart_result.result
|
|
165
137
|
|
|
166
|
-
# Create fallback FixResult
|
|
167
138
|
return FixResult(
|
|
168
139
|
success=smart_result.success,
|
|
169
140
|
confidence=smart_result.confidence,
|
|
@@ -181,7 +152,6 @@ class IntelligentAgentSystem:
|
|
|
181
152
|
description: str,
|
|
182
153
|
context: TaskContext | None = None,
|
|
183
154
|
) -> tuple[str, float] | None:
|
|
184
|
-
"""Get the best agent for a task without executing it."""
|
|
185
155
|
await self.initialize()
|
|
186
156
|
|
|
187
157
|
task = TaskDescription(description=description, context=context)
|
|
@@ -192,14 +162,12 @@ class IntelligentAgentSystem:
|
|
|
192
162
|
return None
|
|
193
163
|
|
|
194
164
|
async def analyze_task_complexity(self, description: str) -> dict[str, t.Any]:
|
|
195
|
-
"""Analyze a task's complexity and provide recommendations."""
|
|
196
165
|
await self.initialize()
|
|
197
166
|
|
|
198
167
|
task = TaskDescription(description=description)
|
|
199
168
|
return await self.orchestrator.selector.analyze_task_complexity(task)
|
|
200
169
|
|
|
201
170
|
def _map_issue_to_task_context(self, issue: Issue) -> TaskContext | None:
|
|
202
|
-
"""Map crackerjack Issue type to TaskContext."""
|
|
203
171
|
from crackerjack.agents.base import IssueType
|
|
204
172
|
|
|
205
173
|
mapping = {
|
|
@@ -220,7 +188,6 @@ class IntelligentAgentSystem:
|
|
|
220
188
|
return mapping.get(issue.type) or TaskContext.GENERAL
|
|
221
189
|
|
|
222
190
|
def _map_severity_to_priority(self, severity: t.Any) -> int:
|
|
223
|
-
"""Map crackerjack Priority to task priority."""
|
|
224
191
|
from crackerjack.agents.base import Priority
|
|
225
192
|
|
|
226
193
|
mapping = {
|
|
@@ -232,7 +199,6 @@ class IntelligentAgentSystem:
|
|
|
232
199
|
return mapping.get(severity) or 50
|
|
233
200
|
|
|
234
201
|
async def get_system_status(self) -> dict[str, t.Any]:
|
|
235
|
-
"""Get comprehensive system status."""
|
|
236
202
|
await self.initialize()
|
|
237
203
|
|
|
238
204
|
registry_stats = self.registry.get_agent_stats()
|
|
@@ -247,12 +213,10 @@ class IntelligentAgentSystem:
|
|
|
247
213
|
}
|
|
248
214
|
|
|
249
215
|
|
|
250
|
-
# Global intelligent agent system
|
|
251
216
|
_intelligent_system_instance: IntelligentAgentSystem | None = None
|
|
252
217
|
|
|
253
218
|
|
|
254
219
|
async def get_intelligent_agent_system() -> IntelligentAgentSystem:
|
|
255
|
-
"""Get or create the global intelligent agent system."""
|
|
256
220
|
global _intelligent_system_instance
|
|
257
221
|
|
|
258
222
|
if _intelligent_system_instance is None:
|
|
@@ -261,12 +225,10 @@ async def get_intelligent_agent_system() -> IntelligentAgentSystem:
|
|
|
261
225
|
return _intelligent_system_instance
|
|
262
226
|
|
|
263
227
|
|
|
264
|
-
# Convenience functions for common use cases
|
|
265
228
|
async def smart_fix_issue(
|
|
266
229
|
issue: Issue,
|
|
267
230
|
context: AgentContext,
|
|
268
231
|
) -> FixResult:
|
|
269
|
-
"""Fix an issue using intelligent agent selection."""
|
|
270
232
|
system = await get_intelligent_agent_system()
|
|
271
233
|
return await system.handle_crackerjack_issue(issue, context)
|
|
272
234
|
|
|
@@ -276,7 +238,6 @@ async def smart_execute_task(
|
|
|
276
238
|
context: TaskContext | None = None,
|
|
277
239
|
strategy: ExecutionStrategy = ExecutionStrategy.SINGLE_BEST,
|
|
278
240
|
) -> SmartAgentResult:
|
|
279
|
-
"""Execute a task using intelligent agent selection."""
|
|
280
241
|
system = await get_intelligent_agent_system()
|
|
281
242
|
return await system.execute_smart_task(description, context, strategy)
|
|
282
243
|
|
|
@@ -285,6 +246,5 @@ async def get_smart_recommendation(
|
|
|
285
246
|
description: str,
|
|
286
247
|
context: TaskContext | None = None,
|
|
287
248
|
) -> tuple[str, float] | None:
|
|
288
|
-
"""Get a smart agent recommendation without executing."""
|
|
289
249
|
system = await get_intelligent_agent_system()
|
|
290
250
|
return await system.get_best_agent_for_task(description, context)
|
crackerjack/interactive.py
CHANGED
|
@@ -311,12 +311,12 @@ class WorkflowManager:
|
|
|
311
311
|
|
|
312
312
|
def _handle_task_without_executor(self, task: Task) -> bool:
|
|
313
313
|
task.skip()
|
|
314
|
-
self.console.print(f"[yellow]⏭️ Skipped {task.name} (no executor)[/yellow]")
|
|
314
|
+
self.console.print(f"[yellow]⏭️ Skipped {task.name} (no executor)[/ yellow]")
|
|
315
315
|
return True
|
|
316
316
|
|
|
317
317
|
def _execute_task_with_executor(self, task: Task) -> bool:
|
|
318
318
|
task.start()
|
|
319
|
-
self.console.print(f"[blue]🔄 Running {task.name}...[/blue]")
|
|
319
|
+
self.console.print(f"[blue]🔄 Running {task.name}...[/ blue]")
|
|
320
320
|
|
|
321
321
|
try:
|
|
322
322
|
return self._try_execute_task(task)
|
|
@@ -334,9 +334,9 @@ class WorkflowManager:
|
|
|
334
334
|
def _display_task_result(self, task: Task, success: bool) -> None:
|
|
335
335
|
if success:
|
|
336
336
|
duration_str = f" ({task.duration: .1f}s)" if task.duration else ""
|
|
337
|
-
self.console.print(f"[green]✅ {task.name}{duration_str}[/green]")
|
|
337
|
+
self.console.print(f"[green]✅ {task.name}{duration_str}[/ green]")
|
|
338
338
|
else:
|
|
339
|
-
self.console.print(f"[red]❌ {task.name} failed[/red]")
|
|
339
|
+
self.console.print(f"[red]❌ {task.name} failed[/ red]")
|
|
340
340
|
|
|
341
341
|
def _handle_task_exception(self, task: Task, e: Exception) -> bool:
|
|
342
342
|
error = CrackerjackError(
|
|
@@ -344,7 +344,7 @@ class WorkflowManager:
|
|
|
344
344
|
error_code=ErrorCode.COMMAND_EXECUTION_ERROR,
|
|
345
345
|
)
|
|
346
346
|
task.fail(error)
|
|
347
|
-
self.console.print(f"[red]💥 {task.name} crashed: {e}[/red]")
|
|
347
|
+
self.console.print(f"[red]💥 {task.name} crashed: {e}[/ red]")
|
|
348
348
|
return False
|
|
349
349
|
|
|
350
350
|
def display_task_tree(self) -> None:
|
|
@@ -587,11 +587,11 @@ class InteractiveCLI:
|
|
|
587
587
|
)
|
|
588
588
|
self.create_dynamic_workflow(options)
|
|
589
589
|
|
|
590
|
-
self.console.print("[bold blue]🚀 Starting Interactive Workflow[/bold blue]")
|
|
590
|
+
self.console.print("[bold blue]🚀 Starting Interactive Workflow[/ bold blue]")
|
|
591
591
|
self.workflow.display_task_tree()
|
|
592
592
|
|
|
593
593
|
if not Confirm.ask("Continue with workflow?"):
|
|
594
|
-
self.console.print("[yellow]Workflow cancelled by user[/yellow]")
|
|
594
|
+
self.console.print("[yellow]Workflow cancelled by user[/ yellow]")
|
|
595
595
|
return False
|
|
596
596
|
|
|
597
597
|
return self._execute_workflow_loop()
|
|
@@ -626,7 +626,7 @@ class InteractiveCLI:
|
|
|
626
626
|
]
|
|
627
627
|
|
|
628
628
|
if pending_tasks:
|
|
629
|
-
self.console.print("[red]❌ Workflow stuck
|
|
629
|
+
self.console.print("[red]❌ Workflow stuck-unresolved dependencies[/ red]")
|
|
630
630
|
return False
|
|
631
631
|
return True
|
|
632
632
|
|
|
@@ -645,7 +645,7 @@ class InteractiveCLI:
|
|
|
645
645
|
def _display_workflow_summary(self) -> None:
|
|
646
646
|
summary = self.workflow.get_workflow_summary()
|
|
647
647
|
|
|
648
|
-
self.console.print("\n[bold]📊 Workflow Summary[/bold]")
|
|
648
|
+
self.console.print("\n[bold]📊 Workflow Summary[/ bold]")
|
|
649
649
|
|
|
650
650
|
table = Table(show_header=True, header_style="bold magenta")
|
|
651
651
|
table.add_column("Status", style="cyan")
|