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
crackerjack/__main__.py
CHANGED
|
@@ -35,7 +35,6 @@ def _handle_monitoring_commands(
|
|
|
35
35
|
watchdog: bool,
|
|
36
36
|
dev: bool,
|
|
37
37
|
) -> bool:
|
|
38
|
-
"""Handle monitoring commands."""
|
|
39
38
|
if monitor:
|
|
40
39
|
handle_monitor_mode(dev_mode=dev)
|
|
41
40
|
return True
|
|
@@ -57,7 +56,6 @@ def _handle_websocket_commands(
|
|
|
57
56
|
restart_websocket_server: bool,
|
|
58
57
|
websocket_port: int | None,
|
|
59
58
|
) -> bool:
|
|
60
|
-
"""Handle WebSocket server commands."""
|
|
61
59
|
if start_websocket_server:
|
|
62
60
|
port = websocket_port or 8675
|
|
63
61
|
handle_start_websocket_server(port)
|
|
@@ -78,7 +76,6 @@ def _handle_mcp_commands(
|
|
|
78
76
|
restart_mcp_server: bool,
|
|
79
77
|
websocket_port: int | None,
|
|
80
78
|
) -> bool:
|
|
81
|
-
"""Handle MCP server commands."""
|
|
82
79
|
if start_mcp_server:
|
|
83
80
|
handle_mcp_server(websocket_port)
|
|
84
81
|
return True
|
|
@@ -105,7 +102,6 @@ def _handle_server_commands(
|
|
|
105
102
|
websocket_port: int | None,
|
|
106
103
|
dev: bool,
|
|
107
104
|
) -> bool:
|
|
108
|
-
"""Handle server-related commands. Returns True if a server command was handled."""
|
|
109
105
|
return (
|
|
110
106
|
_handle_monitoring_commands(monitor, enhanced_monitor, dashboard, watchdog, dev)
|
|
111
107
|
or _handle_websocket_commands(
|
|
@@ -130,6 +126,7 @@ def main(
|
|
|
130
126
|
no_config_updates: bool = CLI_OPTIONS["no_config_updates"],
|
|
131
127
|
update_precommit: bool = CLI_OPTIONS["update_precommit"],
|
|
132
128
|
verbose: bool = CLI_OPTIONS["verbose"],
|
|
129
|
+
debug: bool = CLI_OPTIONS["debug"],
|
|
133
130
|
publish: BumpOption | None = CLI_OPTIONS["publish"],
|
|
134
131
|
all: BumpOption | None = CLI_OPTIONS["all"],
|
|
135
132
|
bump: BumpOption | None = CLI_OPTIONS["bump"],
|
|
@@ -172,6 +169,12 @@ def main(
|
|
|
172
169
|
coverage_goal: float | None = CLI_OPTIONS["coverage_goal"],
|
|
173
170
|
no_coverage_ratchet: bool = CLI_OPTIONS["no_coverage_ratchet"],
|
|
174
171
|
boost_coverage: bool = CLI_OPTIONS["boost_coverage"],
|
|
172
|
+
disable_global_locks: bool = CLI_OPTIONS["disable_global_locks"],
|
|
173
|
+
global_lock_timeout: int = CLI_OPTIONS["global_lock_timeout"],
|
|
174
|
+
global_lock_cleanup: bool = CLI_OPTIONS["global_lock_cleanup"],
|
|
175
|
+
global_lock_dir: str | None = CLI_OPTIONS["global_lock_dir"],
|
|
176
|
+
quick: bool = CLI_OPTIONS["quick"],
|
|
177
|
+
thorough: bool = CLI_OPTIONS["thorough"],
|
|
175
178
|
) -> None:
|
|
176
179
|
options = create_options(
|
|
177
180
|
commit,
|
|
@@ -179,6 +182,7 @@ def main(
|
|
|
179
182
|
no_config_updates,
|
|
180
183
|
update_precommit,
|
|
181
184
|
verbose,
|
|
185
|
+
debug,
|
|
182
186
|
publish,
|
|
183
187
|
all,
|
|
184
188
|
bump,
|
|
@@ -209,15 +213,27 @@ def main(
|
|
|
209
213
|
coverage_goal,
|
|
210
214
|
no_coverage_ratchet,
|
|
211
215
|
boost_coverage,
|
|
216
|
+
disable_global_locks,
|
|
217
|
+
global_lock_timeout,
|
|
218
|
+
global_lock_cleanup,
|
|
219
|
+
global_lock_dir,
|
|
220
|
+
quick,
|
|
221
|
+
thorough,
|
|
212
222
|
)
|
|
213
223
|
|
|
214
224
|
if ai_debug:
|
|
215
225
|
ai_agent = True
|
|
216
226
|
verbose = True
|
|
227
|
+
# Update the options object to reflect the verbose setting
|
|
228
|
+
options.verbose = True
|
|
217
229
|
|
|
218
|
-
|
|
230
|
+
# If debug flag is set, enable verbose mode as well
|
|
231
|
+
if debug:
|
|
232
|
+
verbose = True
|
|
233
|
+
options.verbose = True
|
|
234
|
+
|
|
235
|
+
setup_ai_agent_env(ai_agent, verbose or ai_debug or debug)
|
|
219
236
|
|
|
220
|
-
# Handle server commands
|
|
221
237
|
if _handle_server_commands(
|
|
222
238
|
monitor,
|
|
223
239
|
enhanced_monitor,
|
|
@@ -234,7 +250,6 @@ def main(
|
|
|
234
250
|
):
|
|
235
251
|
return
|
|
236
252
|
|
|
237
|
-
# Handle main workflow
|
|
238
253
|
if interactive:
|
|
239
254
|
handle_interactive_mode(options)
|
|
240
255
|
else:
|
|
@@ -242,7 +257,6 @@ def main(
|
|
|
242
257
|
|
|
243
258
|
|
|
244
259
|
def cli() -> None:
|
|
245
|
-
"""Entry point for console script."""
|
|
246
260
|
app()
|
|
247
261
|
|
|
248
262
|
|
crackerjack/agents/__init__.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# Import all agent modules to trigger registration
|
|
2
1
|
from . import (
|
|
3
2
|
architect_agent,
|
|
4
3
|
documentation_agent,
|
|
@@ -20,12 +19,10 @@ __all__ = [
|
|
|
20
19
|
"AgentCoordinator",
|
|
21
20
|
"AgentTracker",
|
|
22
21
|
"FixResult",
|
|
23
|
-
# Exported classes
|
|
24
22
|
"Issue",
|
|
25
23
|
"IssueType",
|
|
26
24
|
"Priority",
|
|
27
25
|
"SubAgent",
|
|
28
|
-
# Agent modules (imported for registration)
|
|
29
26
|
"architect_agent",
|
|
30
27
|
"documentation_agent",
|
|
31
28
|
"dry_agent",
|
|
@@ -5,15 +5,7 @@ from .proactive_agent import ProactiveAgent
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class ArchitectAgent(ProactiveAgent):
|
|
8
|
-
"""Agent that provides architectural planning and guidance.
|
|
9
|
-
|
|
10
|
-
This agent bridges to the external crackerjack-architect specialist
|
|
11
|
-
for complex architectural decisions while handling simpler planning
|
|
12
|
-
internally through cached patterns.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
8
|
def get_supported_types(self) -> set[IssueType]:
|
|
16
|
-
"""Support all issue types for architectural planning."""
|
|
17
9
|
return {
|
|
18
10
|
IssueType.COMPLEXITY,
|
|
19
11
|
IssueType.DRY_VIOLATION,
|
|
@@ -30,58 +22,39 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
30
22
|
}
|
|
31
23
|
|
|
32
24
|
async def can_handle(self, issue: Issue) -> float:
|
|
33
|
-
"""Determine confidence in handling this issue architecturally."""
|
|
34
|
-
# High confidence for complex issues that benefit from planning
|
|
35
25
|
if issue.type == IssueType.COMPLEXITY:
|
|
36
26
|
return 0.9
|
|
37
27
|
|
|
38
|
-
# High confidence for DRY violations (architectural)
|
|
39
28
|
if issue.type == IssueType.DRY_VIOLATION:
|
|
40
29
|
return 0.85
|
|
41
30
|
|
|
42
|
-
# Medium-high confidence for performance issues
|
|
43
31
|
if issue.type == IssueType.PERFORMANCE:
|
|
44
32
|
return 0.8
|
|
45
33
|
|
|
46
|
-
# Medium confidence for security (architectural patterns)
|
|
47
34
|
if issue.type == IssueType.SECURITY:
|
|
48
35
|
return 0.75
|
|
49
36
|
|
|
50
|
-
# Lower confidence for formatting/imports (less architectural)
|
|
51
37
|
if issue.type in {IssueType.FORMATTING, IssueType.IMPORT_ERROR}:
|
|
52
38
|
return 0.4
|
|
53
39
|
|
|
54
|
-
# Medium confidence for other types
|
|
55
40
|
return 0.6
|
|
56
41
|
|
|
57
42
|
async def plan_before_action(self, issue: Issue) -> dict[str, t.Any]:
|
|
58
|
-
"""Create architectural plan for fixing the issue."""
|
|
59
|
-
# Check if this is a complex issue requiring external specialist
|
|
60
43
|
if await self._needs_external_specialist(issue):
|
|
61
44
|
return await self._get_specialist_plan(issue)
|
|
62
45
|
|
|
63
|
-
# Use internal planning for simpler issues
|
|
64
46
|
return await self._get_internal_plan(issue)
|
|
65
47
|
|
|
66
48
|
async def _needs_external_specialist(self, issue: Issue) -> bool:
|
|
67
|
-
"""Determine if external crackerjack-architect specialist is needed."""
|
|
68
|
-
# Always use specialist for complexity issues
|
|
69
49
|
if issue.type == IssueType.COMPLEXITY:
|
|
70
50
|
return True
|
|
71
51
|
|
|
72
|
-
# Use specialist for DRY violations
|
|
73
52
|
if issue.type == IssueType.DRY_VIOLATION:
|
|
74
53
|
return True
|
|
75
54
|
|
|
76
|
-
# Use specialist for multiple related issues
|
|
77
|
-
# (This would be determined by the coordinator in practice)
|
|
78
55
|
return False
|
|
79
56
|
|
|
80
57
|
async def _get_specialist_plan(self, issue: Issue) -> dict[str, t.Any]:
|
|
81
|
-
"""Get plan from external crackerjack-architect specialist."""
|
|
82
|
-
# This would use the Task tool to invoke crackerjack-architect
|
|
83
|
-
# For now, return a structured plan that mimics what the specialist would provide
|
|
84
|
-
|
|
85
58
|
plan = {
|
|
86
59
|
"strategy": "external_specialist_guided",
|
|
87
60
|
"specialist": "crackerjack-architect",
|
|
@@ -95,7 +68,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
95
68
|
return plan
|
|
96
69
|
|
|
97
70
|
async def _get_internal_plan(self, issue: Issue) -> dict[str, t.Any]:
|
|
98
|
-
"""Create plan using internal architectural knowledge."""
|
|
99
71
|
plan = {
|
|
100
72
|
"strategy": "internal_pattern_based",
|
|
101
73
|
"approach": self._get_internal_approach(issue),
|
|
@@ -108,7 +80,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
108
80
|
return plan
|
|
109
81
|
|
|
110
82
|
def _get_specialist_approach(self, issue: Issue) -> str:
|
|
111
|
-
"""Get the approach that crackerjack-architect would recommend."""
|
|
112
83
|
if issue.type == IssueType.COMPLEXITY:
|
|
113
84
|
return "break_into_helper_methods"
|
|
114
85
|
elif issue.type == IssueType.DRY_VIOLATION:
|
|
@@ -120,7 +91,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
120
91
|
return "apply_clean_code_principles"
|
|
121
92
|
|
|
122
93
|
def _get_internal_approach(self, issue: Issue) -> str:
|
|
123
|
-
"""Get internal approach for simpler issues."""
|
|
124
94
|
return {
|
|
125
95
|
IssueType.FORMATTING: "apply_standard_formatting",
|
|
126
96
|
IssueType.IMPORT_ERROR: "optimize_imports",
|
|
@@ -131,7 +101,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
131
101
|
}.get(issue.type, "apply_standard_fix")
|
|
132
102
|
|
|
133
103
|
def _get_recommended_patterns(self, issue: Issue) -> list[str]:
|
|
134
|
-
"""Get patterns recommended by crackerjack-architect."""
|
|
135
104
|
return {
|
|
136
105
|
IssueType.COMPLEXITY: [
|
|
137
106
|
"extract_method",
|
|
@@ -160,7 +129,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
160
129
|
}.get(issue.type, ["standard_patterns"])
|
|
161
130
|
|
|
162
131
|
def _get_cached_patterns_for_issue(self, issue: Issue) -> list[str]:
|
|
163
|
-
"""Get cached patterns that match this issue type."""
|
|
164
132
|
cached = self.get_cached_patterns()
|
|
165
133
|
matching_patterns = []
|
|
166
134
|
|
|
@@ -173,7 +141,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
173
141
|
return matching_patterns or ["default_pattern"]
|
|
174
142
|
|
|
175
143
|
def _analyze_dependencies(self, issue: Issue) -> list[str]:
|
|
176
|
-
"""Analyze what other changes might be needed."""
|
|
177
144
|
dependencies = []
|
|
178
145
|
|
|
179
146
|
if issue.type == IssueType.COMPLEXITY:
|
|
@@ -193,7 +160,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
193
160
|
return dependencies
|
|
194
161
|
|
|
195
162
|
def _identify_risks(self, issue: Issue) -> list[str]:
|
|
196
|
-
"""Identify potential risks in fixing this issue."""
|
|
197
163
|
risks = []
|
|
198
164
|
|
|
199
165
|
if issue.type == IssueType.COMPLEXITY:
|
|
@@ -211,7 +177,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
211
177
|
return risks
|
|
212
178
|
|
|
213
179
|
def _get_validation_steps(self, issue: Issue) -> list[str]:
|
|
214
|
-
"""Get steps to validate the fix."""
|
|
215
180
|
return [
|
|
216
181
|
"run_fast_hooks",
|
|
217
182
|
"run_full_tests",
|
|
@@ -221,13 +186,11 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
221
186
|
]
|
|
222
187
|
|
|
223
188
|
async def analyze_and_fix(self, issue: Issue) -> FixResult:
|
|
224
|
-
"""Standard fix method - delegates to proactive version."""
|
|
225
189
|
return await self.analyze_and_fix_proactively(issue)
|
|
226
190
|
|
|
227
191
|
async def _execute_with_plan(
|
|
228
192
|
self, issue: Issue, plan: dict[str, t.Any]
|
|
229
193
|
) -> FixResult:
|
|
230
|
-
"""Execute fix following the architectural plan."""
|
|
231
194
|
strategy = plan.get("strategy", "internal_pattern_based")
|
|
232
195
|
|
|
233
196
|
if strategy == "external_specialist_guided":
|
|
@@ -237,10 +200,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
237
200
|
async def _execute_specialist_guided_fix(
|
|
238
201
|
self, issue: Issue, plan: dict[str, t.Any]
|
|
239
202
|
) -> FixResult:
|
|
240
|
-
"""Execute fix guided by external specialist plan."""
|
|
241
|
-
# This would invoke the actual Task tool with crackerjack-architect
|
|
242
|
-
# For now, return a structured result indicating the plan was followed
|
|
243
|
-
|
|
244
203
|
return FixResult(
|
|
245
204
|
success=True,
|
|
246
205
|
confidence=0.9,
|
|
@@ -260,7 +219,6 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
260
219
|
async def _execute_pattern_based_fix(
|
|
261
220
|
self, issue: Issue, plan: dict[str, t.Any]
|
|
262
221
|
) -> FixResult:
|
|
263
|
-
"""Execute fix using cached patterns and internal logic."""
|
|
264
222
|
patterns = plan.get("patterns", [])
|
|
265
223
|
approach = plan.get("approach", "standard")
|
|
266
224
|
|
|
@@ -277,5 +235,4 @@ class ArchitectAgent(ProactiveAgent):
|
|
|
277
235
|
)
|
|
278
236
|
|
|
279
237
|
|
|
280
|
-
# Register the agent
|
|
281
238
|
agent_registry.register(ArchitectAgent)
|
crackerjack/agents/base.py
CHANGED
|
@@ -27,6 +27,7 @@ class IssueType(Enum):
|
|
|
27
27
|
DOCUMENTATION = "documentation"
|
|
28
28
|
TEST_ORGANIZATION = "test_organization"
|
|
29
29
|
COVERAGE_IMPROVEMENT = "coverage_improvement"
|
|
30
|
+
REGEX_VALIDATION = "regex_validation"
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
@dataclass
|
|
@@ -40,10 +41,6 @@ class Issue:
|
|
|
40
41
|
details: list[str] = field(default_factory=list)
|
|
41
42
|
stage: str = "unknown"
|
|
42
43
|
|
|
43
|
-
@property
|
|
44
|
-
def context_key(self) -> str:
|
|
45
|
-
return f"{self.type.value}: {self.file_path}: {self.line_number}"
|
|
46
|
-
|
|
47
44
|
|
|
48
45
|
@dataclass
|
|
49
46
|
class FixResult:
|
|
@@ -145,11 +142,9 @@ class SubAgent(ABC):
|
|
|
145
142
|
pass
|
|
146
143
|
|
|
147
144
|
async def plan_before_action(self, issue: Issue) -> dict[str, t.Any]:
|
|
148
|
-
"""Plan actions before executing fixes. Override in subclasses."""
|
|
149
145
|
return {"strategy": "default", "confidence": 0.5}
|
|
150
146
|
|
|
151
147
|
def get_cached_patterns(self) -> dict[str, t.Any]:
|
|
152
|
-
"""Get cached patterns for this agent. Override in subclasses."""
|
|
153
148
|
return {}
|
|
154
149
|
|
|
155
150
|
|
|
@@ -163,8 +158,5 @@ class AgentRegistry:
|
|
|
163
158
|
def create_all(self, context: AgentContext) -> list[SubAgent]:
|
|
164
159
|
return [agent_cls(context) for agent_cls in self._agents.values()]
|
|
165
160
|
|
|
166
|
-
def get_by_name(self, name: str) -> type[SubAgent] | None:
|
|
167
|
-
return self._agents.get(name)
|
|
168
|
-
|
|
169
161
|
|
|
170
162
|
agent_registry = AgentRegistry()
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
-
import operator
|
|
4
3
|
import typing as t
|
|
5
4
|
from collections import defaultdict
|
|
6
5
|
|
|
@@ -11,7 +10,6 @@ from .base import (
|
|
|
11
10
|
FixResult,
|
|
12
11
|
Issue,
|
|
13
12
|
IssueType,
|
|
14
|
-
Priority,
|
|
15
13
|
SubAgent,
|
|
16
14
|
agent_registry,
|
|
17
15
|
)
|
|
@@ -27,7 +25,7 @@ class AgentCoordinator:
|
|
|
27
25
|
self._collaboration_threshold = 0.7
|
|
28
26
|
self.tracker = get_agent_tracker()
|
|
29
27
|
self.debugger = get_ai_agent_debugger()
|
|
30
|
-
self.proactive_mode = True
|
|
28
|
+
self.proactive_mode = True
|
|
31
29
|
|
|
32
30
|
def initialize_agents(self) -> None:
|
|
33
31
|
self.agents = agent_registry.create_all(self.context)
|
|
@@ -62,40 +60,7 @@ class AgentCoordinator:
|
|
|
62
60
|
|
|
63
61
|
return overall_result
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
if not self.agents:
|
|
67
|
-
self.initialize_agents()
|
|
68
|
-
|
|
69
|
-
cache_key = issue.context_key
|
|
70
|
-
if cache_key in self._issue_cache:
|
|
71
|
-
cached_result = self._issue_cache[cache_key]
|
|
72
|
-
self.logger.info(f"Using cached result for {cache_key}")
|
|
73
|
-
self.tracker.track_cache_hit()
|
|
74
|
-
return cached_result
|
|
75
|
-
|
|
76
|
-
self.tracker.track_cache_miss()
|
|
77
|
-
|
|
78
|
-
agent_scores = await self._evaluate_agents_for_issue(issue)
|
|
79
|
-
|
|
80
|
-
if not agent_scores:
|
|
81
|
-
self.logger.warning(f"No agents can handle issue: {issue.message}")
|
|
82
|
-
return FixResult(
|
|
83
|
-
success=False,
|
|
84
|
-
confidence=0.0,
|
|
85
|
-
remaining_issues=[f"No agent available for: {issue.message}"],
|
|
86
|
-
recommendations=["Manual intervention required"],
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
best_agent, best_score = agent_scores[0]
|
|
90
|
-
|
|
91
|
-
if best_score >= self._collaboration_threshold:
|
|
92
|
-
result = await self._handle_with_single_agent(best_agent, issue)
|
|
93
|
-
else:
|
|
94
|
-
result = await self._handle_with_collaboration(agent_scores[:3], issue)
|
|
95
|
-
|
|
96
|
-
self._issue_cache[cache_key] = result
|
|
97
|
-
|
|
98
|
-
return result
|
|
63
|
+
# Removed unused method: handle_single_issue
|
|
99
64
|
|
|
100
65
|
async def _handle_issues_by_type(
|
|
101
66
|
self,
|
|
@@ -139,23 +104,6 @@ class AgentCoordinator:
|
|
|
139
104
|
|
|
140
105
|
return combined_result
|
|
141
106
|
|
|
142
|
-
async def _evaluate_agents_for_issue(
|
|
143
|
-
self,
|
|
144
|
-
issue: Issue,
|
|
145
|
-
) -> list[tuple[SubAgent, float]]:
|
|
146
|
-
evaluations: list[tuple[SubAgent, float]] = []
|
|
147
|
-
|
|
148
|
-
for agent in self.agents:
|
|
149
|
-
try:
|
|
150
|
-
confidence = await agent.can_handle(issue)
|
|
151
|
-
if confidence > 0.0:
|
|
152
|
-
evaluations.append((agent, confidence))
|
|
153
|
-
except Exception as e:
|
|
154
|
-
self.logger.exception(f"Error evaluating {agent.name} for issue: {e}")
|
|
155
|
-
|
|
156
|
-
evaluations.sort(key=operator.itemgetter(1), reverse=True)
|
|
157
|
-
return evaluations
|
|
158
|
-
|
|
159
107
|
async def _find_best_specialist(
|
|
160
108
|
self,
|
|
161
109
|
specialists: list[SubAgent],
|
|
@@ -226,45 +174,6 @@ class AgentCoordinator:
|
|
|
226
174
|
self.tracker.track_agent_complete(agent.name, error_result)
|
|
227
175
|
return error_result
|
|
228
176
|
|
|
229
|
-
async def _handle_with_collaboration(
|
|
230
|
-
self,
|
|
231
|
-
agent_scores: list[tuple[SubAgent, float]],
|
|
232
|
-
issue: Issue,
|
|
233
|
-
) -> FixResult:
|
|
234
|
-
self.logger.info(
|
|
235
|
-
f"Using collaborative approach for issue: {issue.message[:100]}",
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
results: list[FixResult] = []
|
|
239
|
-
|
|
240
|
-
for agent, _ in agent_scores:
|
|
241
|
-
try:
|
|
242
|
-
result = await agent.analyze_and_fix(issue)
|
|
243
|
-
results.append(result)
|
|
244
|
-
|
|
245
|
-
if result.success and result.confidence >= 0.8:
|
|
246
|
-
self.logger.info(f"{agent.name} solved issue with high confidence")
|
|
247
|
-
break
|
|
248
|
-
|
|
249
|
-
except Exception as e:
|
|
250
|
-
self.logger.exception(f"Collaborative agent {agent.name} failed: {e}")
|
|
251
|
-
results.append(
|
|
252
|
-
FixResult(
|
|
253
|
-
success=False,
|
|
254
|
-
confidence=0.0,
|
|
255
|
-
remaining_issues=[f"{agent.name} failed: {e}"],
|
|
256
|
-
),
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
if not results:
|
|
260
|
-
return FixResult(success=False, confidence=0.0)
|
|
261
|
-
|
|
262
|
-
combined_result = results[0]
|
|
263
|
-
for result in results[1:]:
|
|
264
|
-
combined_result = combined_result.merge_with(result)
|
|
265
|
-
|
|
266
|
-
return combined_result
|
|
267
|
-
|
|
268
177
|
def _group_issues_by_type(
|
|
269
178
|
self,
|
|
270
179
|
issues: list[Issue],
|
|
@@ -286,34 +195,7 @@ class AgentCoordinator:
|
|
|
286
195
|
}
|
|
287
196
|
return capabilities
|
|
288
197
|
|
|
289
|
-
def clear_cache(self) -> None:
|
|
290
|
-
self._issue_cache.clear()
|
|
291
|
-
self.logger.info("Cleared issue cache")
|
|
292
|
-
|
|
293
|
-
async def test_agent_connectivity(self) -> dict[str, bool]:
|
|
294
|
-
if not self.agents:
|
|
295
|
-
self.initialize_agents()
|
|
296
|
-
|
|
297
|
-
test_issue = Issue(
|
|
298
|
-
id="test",
|
|
299
|
-
type=IssueType.FORMATTING,
|
|
300
|
-
severity=Priority.LOW,
|
|
301
|
-
message="Test connectivity",
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
connectivity = {}
|
|
305
|
-
for agent in self.agents:
|
|
306
|
-
try:
|
|
307
|
-
confidence = await agent.can_handle(test_issue)
|
|
308
|
-
connectivity[agent.name] = confidence >= 0.0
|
|
309
|
-
except Exception as e:
|
|
310
|
-
self.logger.exception(f"Connectivity test failed for {agent.name}: {e}")
|
|
311
|
-
connectivity[agent.name] = False
|
|
312
|
-
|
|
313
|
-
return connectivity
|
|
314
|
-
|
|
315
198
|
async def handle_issues_proactively(self, issues: list[Issue]) -> FixResult:
|
|
316
|
-
"""Handle issues with proactive planning phase."""
|
|
317
199
|
if not self.proactive_mode:
|
|
318
200
|
return await self.handle_issues(issues)
|
|
319
201
|
|
|
@@ -325,13 +207,10 @@ class AgentCoordinator:
|
|
|
325
207
|
|
|
326
208
|
self.logger.info(f"Handling {len(issues)} issues with proactive planning")
|
|
327
209
|
|
|
328
|
-
# Phase 1: Create architectural plan
|
|
329
210
|
architectural_plan = await self._create_architectural_plan(issues)
|
|
330
211
|
|
|
331
|
-
# Phase 2: Apply fixes following the plan
|
|
332
212
|
overall_result = await self._apply_fixes_with_plan(issues, architectural_plan)
|
|
333
213
|
|
|
334
|
-
# Phase 3: Validate against plan
|
|
335
214
|
validation_result = await self._validate_against_plan(
|
|
336
215
|
overall_result, architectural_plan
|
|
337
216
|
)
|
|
@@ -339,14 +218,12 @@ class AgentCoordinator:
|
|
|
339
218
|
return validation_result
|
|
340
219
|
|
|
341
220
|
async def _create_architectural_plan(self, issues: list[Issue]) -> dict[str, t.Any]:
|
|
342
|
-
"""Create architectural plan using ArchitectAgent."""
|
|
343
221
|
architect = self._get_architect_agent()
|
|
344
222
|
|
|
345
223
|
if not architect:
|
|
346
224
|
self.logger.warning("No ArchitectAgent available for planning")
|
|
347
225
|
return {"strategy": "reactive_fallback", "patterns": []}
|
|
348
226
|
|
|
349
|
-
# Analyze all issues together for comprehensive planning
|
|
350
227
|
complex_issues = [
|
|
351
228
|
issue
|
|
352
229
|
for issue in issues
|
|
@@ -357,14 +234,11 @@ class AgentCoordinator:
|
|
|
357
234
|
if not complex_issues:
|
|
358
235
|
return {"strategy": "simple_fixes", "patterns": ["standard_patterns"]}
|
|
359
236
|
|
|
360
|
-
# Use the first complex issue for planning (represents the architectural challenge)
|
|
361
237
|
primary_issue = complex_issues[0]
|
|
362
238
|
|
|
363
239
|
try:
|
|
364
|
-
# Get plan from ArchitectAgent
|
|
365
240
|
plan = await architect.plan_before_action(primary_issue)
|
|
366
241
|
|
|
367
|
-
# Extend plan to cover all issues
|
|
368
242
|
plan["all_issues"] = [issue.id for issue in issues]
|
|
369
243
|
plan["issue_types"] = list({issue.type.value for issue in issues})
|
|
370
244
|
|
|
@@ -380,16 +254,13 @@ class AgentCoordinator:
|
|
|
380
254
|
async def _apply_fixes_with_plan(
|
|
381
255
|
self, issues: list[Issue], plan: dict[str, t.Any]
|
|
382
256
|
) -> FixResult:
|
|
383
|
-
"""Apply fixes following the architectural plan."""
|
|
384
257
|
strategy = plan.get("strategy", "reactive_fallback")
|
|
385
258
|
|
|
386
259
|
if strategy == "reactive_fallback":
|
|
387
|
-
# Fallback to standard processing
|
|
388
260
|
return await self.handle_issues(issues)
|
|
389
261
|
|
|
390
262
|
self.logger.info(f"Applying fixes with {strategy} strategy")
|
|
391
263
|
|
|
392
|
-
# Group issues by priority based on plan
|
|
393
264
|
prioritized_issues = self._prioritize_issues_by_plan(issues, plan)
|
|
394
265
|
|
|
395
266
|
overall_result = FixResult(success=True, confidence=1.0)
|
|
@@ -398,7 +269,6 @@ class AgentCoordinator:
|
|
|
398
269
|
group_result = await self._handle_issue_group_with_plan(issue_group, plan)
|
|
399
270
|
overall_result = overall_result.merge_with(group_result)
|
|
400
271
|
|
|
401
|
-
# If a critical group fails, consider the overall strategy
|
|
402
272
|
if not group_result.success and self._is_critical_group(issue_group, plan):
|
|
403
273
|
overall_result.success = False
|
|
404
274
|
overall_result.remaining_issues.append(
|
|
@@ -410,7 +280,6 @@ class AgentCoordinator:
|
|
|
410
280
|
async def _validate_against_plan(
|
|
411
281
|
self, result: FixResult, plan: dict[str, t.Any]
|
|
412
282
|
) -> FixResult:
|
|
413
|
-
"""Validate the results against the architectural plan."""
|
|
414
283
|
validation_steps = plan.get("validation", [])
|
|
415
284
|
|
|
416
285
|
if not validation_steps:
|
|
@@ -418,7 +287,6 @@ class AgentCoordinator:
|
|
|
418
287
|
|
|
419
288
|
self.logger.info(f"Validating against plan: {validation_steps}")
|
|
420
289
|
|
|
421
|
-
# Add validation recommendations
|
|
422
290
|
result.recommendations.extend(
|
|
423
291
|
[
|
|
424
292
|
f"Validate with: {', '.join(validation_steps)}",
|
|
@@ -430,7 +298,6 @@ class AgentCoordinator:
|
|
|
430
298
|
return result
|
|
431
299
|
|
|
432
300
|
def _get_architect_agent(self) -> SubAgent | None:
|
|
433
|
-
"""Get the ArchitectAgent from available agents."""
|
|
434
301
|
for agent in self.agents:
|
|
435
302
|
if agent.__class__.__name__ == "ArchitectAgent":
|
|
436
303
|
return agent
|
|
@@ -439,11 +306,9 @@ class AgentCoordinator:
|
|
|
439
306
|
def _prioritize_issues_by_plan(
|
|
440
307
|
self, issues: list[Issue], plan: dict[str, t.Any]
|
|
441
308
|
) -> list[list[Issue]]:
|
|
442
|
-
"""Prioritize issues based on the architectural plan."""
|
|
443
309
|
strategy = plan.get("strategy", "reactive_fallback")
|
|
444
310
|
|
|
445
311
|
if strategy == "external_specialist_guided":
|
|
446
|
-
# Handle complex issues first, then simple ones
|
|
447
312
|
complex_issues = [
|
|
448
313
|
issue
|
|
449
314
|
for issue in issues
|
|
@@ -452,24 +317,20 @@ class AgentCoordinator:
|
|
|
452
317
|
other_issues = [issue for issue in issues if issue not in complex_issues]
|
|
453
318
|
return [complex_issues, other_issues] if complex_issues else [other_issues]
|
|
454
319
|
|
|
455
|
-
# Default: group by type for parallel processing
|
|
456
320
|
groups = self._group_issues_by_type(issues)
|
|
457
321
|
return list(groups.values())
|
|
458
322
|
|
|
459
323
|
async def _handle_issue_group_with_plan(
|
|
460
324
|
self, issues: list[Issue], plan: dict[str, t.Any]
|
|
461
325
|
) -> FixResult:
|
|
462
|
-
"""Handle a group of issues with architectural guidance."""
|
|
463
326
|
if not issues:
|
|
464
327
|
return FixResult(success=True, confidence=1.0)
|
|
465
328
|
|
|
466
|
-
# Get the best agent for this group, preferring ArchitectAgent for complex issues
|
|
467
329
|
representative_issue = issues[0]
|
|
468
330
|
|
|
469
331
|
if self._should_use_architect_for_group(issues, plan):
|
|
470
332
|
architect = self._get_architect_agent()
|
|
471
333
|
if architect:
|
|
472
|
-
# Use architect for the whole group
|
|
473
334
|
group_result = FixResult(success=True, confidence=1.0)
|
|
474
335
|
|
|
475
336
|
for issue in issues:
|
|
@@ -478,20 +339,16 @@ class AgentCoordinator:
|
|
|
478
339
|
|
|
479
340
|
return group_result
|
|
480
341
|
|
|
481
|
-
# Use standard routing for simpler issues
|
|
482
342
|
return await self._handle_issues_by_type(representative_issue.type, issues)
|
|
483
343
|
|
|
484
344
|
def _should_use_architect_for_group(
|
|
485
345
|
self, issues: list[Issue], plan: dict[str, t.Any]
|
|
486
346
|
) -> bool:
|
|
487
|
-
"""Determine if ArchitectAgent should handle this group."""
|
|
488
347
|
strategy = plan.get("strategy", "")
|
|
489
348
|
|
|
490
|
-
# Always use architect for specialist-guided strategies
|
|
491
349
|
if strategy == "external_specialist_guided":
|
|
492
350
|
return True
|
|
493
351
|
|
|
494
|
-
# Use architect for complex or architectural issues
|
|
495
352
|
architectural_types = {
|
|
496
353
|
IssueType.COMPLEXITY,
|
|
497
354
|
IssueType.DRY_VIOLATION,
|
|
@@ -501,12 +358,9 @@ class AgentCoordinator:
|
|
|
501
358
|
return any(issue.type in architectural_types for issue in issues)
|
|
502
359
|
|
|
503
360
|
def _is_critical_group(self, issues: list[Issue], plan: dict[str, t.Any]) -> bool:
|
|
504
|
-
"""Determine if this issue group is critical to the plan."""
|
|
505
|
-
# Complex and DRY violations are typically critical
|
|
506
361
|
critical_types = {IssueType.COMPLEXITY, IssueType.DRY_VIOLATION}
|
|
507
362
|
return any(issue.type in critical_types for issue in issues)
|
|
508
363
|
|
|
509
364
|
def set_proactive_mode(self, enabled: bool) -> None:
|
|
510
|
-
"""Enable or disable proactive planning mode."""
|
|
511
365
|
self.proactive_mode = enabled
|
|
512
366
|
self.logger.info(f"Proactive mode {'enabled' if enabled else 'disabled'}")
|