crackerjack 0.38.15__py3-none-any.whl → 0.39.0__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/__main__.py +134 -13
- crackerjack/agents/__init__.py +2 -0
- crackerjack/agents/base.py +1 -0
- crackerjack/agents/claude_code_bridge.py +319 -0
- crackerjack/agents/coordinator.py +6 -3
- crackerjack/agents/dry_agent.py +187 -3
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/performance_agent.py +324 -3
- crackerjack/agents/refactoring_agent.py +254 -5
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/cli/options.py +27 -0
- crackerjack/cli/semantic_handlers.py +290 -0
- crackerjack/core/async_workflow_orchestrator.py +9 -8
- crackerjack/core/enhanced_container.py +1 -1
- crackerjack/core/phase_coordinator.py +1 -1
- crackerjack/core/proactive_workflow.py +1 -1
- crackerjack/core/workflow_orchestrator.py +9 -6
- crackerjack/documentation/ai_templates.py +1 -1
- crackerjack/interactive.py +1 -1
- crackerjack/mcp/server_core.py +2 -0
- crackerjack/mcp/tools/__init__.py +2 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/models/semantic_models.py +271 -0
- crackerjack/plugins/loader.py +2 -2
- crackerjack/py313.py +4 -1
- crackerjack/services/embeddings.py +444 -0
- crackerjack/services/quality_intelligence.py +11 -1
- crackerjack/services/smart_scheduling.py +1 -1
- crackerjack/services/vector_store.py +681 -0
- crackerjack/slash_commands/run.md +84 -50
- {crackerjack-0.38.15.dist-info → crackerjack-0.39.0.dist-info}/METADATA +7 -2
- {crackerjack-0.38.15.dist-info → crackerjack-0.39.0.dist-info}/RECORD +37 -27
- {crackerjack-0.38.15.dist-info → crackerjack-0.39.0.dist-info}/WHEEL +0 -0
- {crackerjack-0.38.15.dist-info → crackerjack-0.39.0.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.38.15.dist-info → crackerjack-0.39.0.dist-info}/licenses/LICENSE +0 -0
crackerjack/__main__.py
CHANGED
|
@@ -34,6 +34,12 @@ from .cli.handlers import (
|
|
|
34
34
|
handle_stop_zuban_lsp,
|
|
35
35
|
handle_watchdog_mode,
|
|
36
36
|
)
|
|
37
|
+
from .cli.semantic_handlers import (
|
|
38
|
+
handle_remove_from_semantic_index,
|
|
39
|
+
handle_semantic_index,
|
|
40
|
+
handle_semantic_search,
|
|
41
|
+
handle_semantic_stats,
|
|
42
|
+
)
|
|
37
43
|
|
|
38
44
|
console = Console(force_terminal=True)
|
|
39
45
|
app = typer.Typer(
|
|
@@ -1041,17 +1047,10 @@ def _handle_mkdocs_integration(
|
|
|
1041
1047
|
return False
|
|
1042
1048
|
|
|
1043
1049
|
|
|
1044
|
-
def
|
|
1045
|
-
"""Create
|
|
1046
|
-
from logging import getLogger
|
|
1050
|
+
def _create_sync_filesystem_service() -> t.Any:
|
|
1051
|
+
"""Create filesystem service that matches FileSystemServiceProtocol."""
|
|
1047
1052
|
from pathlib import Path
|
|
1048
1053
|
|
|
1049
|
-
from crackerjack.documentation.mkdocs_integration import (
|
|
1050
|
-
MkDocsIntegrationService,
|
|
1051
|
-
MkDocsSiteBuilder,
|
|
1052
|
-
)
|
|
1053
|
-
|
|
1054
|
-
# Create filesystem service that matches FileSystemServiceProtocol
|
|
1055
1054
|
class SyncFileSystemService:
|
|
1056
1055
|
def read_file(self, path: str | Path) -> str:
|
|
1057
1056
|
return Path(path).read_text()
|
|
@@ -1068,7 +1067,12 @@ def _create_mkdocs_services() -> dict[str, t.Any]:
|
|
|
1068
1067
|
def ensure_directory(self, path: str | Path) -> None:
|
|
1069
1068
|
Path(path).mkdir(parents=True, exist_ok=True)
|
|
1070
1069
|
|
|
1071
|
-
|
|
1070
|
+
return SyncFileSystemService()
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
def _create_config_manager() -> t.Any:
|
|
1074
|
+
"""Create config manager that implements ConfigManagerProtocol."""
|
|
1075
|
+
|
|
1072
1076
|
class ConfigManager:
|
|
1073
1077
|
def __init__(self) -> None:
|
|
1074
1078
|
self._config: dict[str, t.Any] = {}
|
|
@@ -1085,11 +1089,48 @@ def _create_mkdocs_services() -> dict[str, t.Any]:
|
|
|
1085
1089
|
def load(self) -> bool:
|
|
1086
1090
|
return True
|
|
1087
1091
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1092
|
+
return ConfigManager()
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def _create_logger_adapter(logger: t.Any) -> t.Any:
|
|
1096
|
+
"""Create logger adapter for protocol compatibility."""
|
|
1097
|
+
|
|
1098
|
+
class LoggerAdapter:
|
|
1099
|
+
def __init__(self, logger: t.Any) -> None:
|
|
1100
|
+
self._logger = logger
|
|
1101
|
+
|
|
1102
|
+
def debug(self, message: str, **kwargs: t.Any) -> None:
|
|
1103
|
+
self._logger.debug(message)
|
|
1104
|
+
|
|
1105
|
+
def info(self, message: str, **kwargs: t.Any) -> None:
|
|
1106
|
+
self._logger.info(message)
|
|
1107
|
+
|
|
1108
|
+
def warning(self, message: str, **kwargs: t.Any) -> None:
|
|
1109
|
+
self._logger.warning(message)
|
|
1110
|
+
|
|
1111
|
+
def error(self, message: str, **kwargs: t.Any) -> None:
|
|
1112
|
+
self._logger.error(message)
|
|
1113
|
+
|
|
1114
|
+
return LoggerAdapter(logger)
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
def _create_mkdocs_services() -> dict[str, t.Any]:
|
|
1118
|
+
"""Create and configure MkDocs services."""
|
|
1119
|
+
from logging import getLogger
|
|
1120
|
+
|
|
1121
|
+
from crackerjack.documentation.mkdocs_integration import (
|
|
1122
|
+
MkDocsIntegrationService,
|
|
1123
|
+
MkDocsSiteBuilder,
|
|
1124
|
+
)
|
|
1125
|
+
|
|
1126
|
+
filesystem = _create_sync_filesystem_service()
|
|
1127
|
+
config_manager = _create_config_manager()
|
|
1090
1128
|
logger = getLogger(__name__)
|
|
1129
|
+
logger_adapter = _create_logger_adapter(logger)
|
|
1091
1130
|
|
|
1092
|
-
integration_service = MkDocsIntegrationService(
|
|
1131
|
+
integration_service = MkDocsIntegrationService(
|
|
1132
|
+
config_manager, filesystem, logger_adapter
|
|
1133
|
+
)
|
|
1093
1134
|
builder = MkDocsSiteBuilder(integration_service)
|
|
1094
1135
|
|
|
1095
1136
|
return {"builder": builder, "filesystem": filesystem, "config": config_manager}
|
|
@@ -1317,6 +1358,11 @@ def main(
|
|
|
1317
1358
|
diff_config: str | None = CLI_OPTIONS["diff_config"],
|
|
1318
1359
|
config_interactive: bool = CLI_OPTIONS["config_interactive"],
|
|
1319
1360
|
refresh_cache: bool = CLI_OPTIONS["refresh_cache"],
|
|
1361
|
+
# Semantic search options
|
|
1362
|
+
index: str | None = CLI_OPTIONS["index"],
|
|
1363
|
+
search: str | None = CLI_OPTIONS["search"],
|
|
1364
|
+
semantic_stats: bool = CLI_OPTIONS["semantic_stats"],
|
|
1365
|
+
remove_from_index: str | None = CLI_OPTIONS["remove_from_index"],
|
|
1320
1366
|
) -> None:
|
|
1321
1367
|
options = create_options(
|
|
1322
1368
|
commit,
|
|
@@ -1409,6 +1455,12 @@ def main(
|
|
|
1409
1455
|
run_tests=run_tests,
|
|
1410
1456
|
)
|
|
1411
1457
|
|
|
1458
|
+
# Add semantic search options to the options object
|
|
1459
|
+
options.index = index
|
|
1460
|
+
options.search = search
|
|
1461
|
+
options.semantic_stats = semantic_stats
|
|
1462
|
+
options.remove_from_index = remove_from_index
|
|
1463
|
+
|
|
1412
1464
|
# Setup debug and verbose flags
|
|
1413
1465
|
ai_fix, verbose = _setup_debug_and_verbose_flags(ai_debug, debug, verbose, options)
|
|
1414
1466
|
setup_ai_agent_env(ai_fix, ai_debug or debug)
|
|
@@ -1442,6 +1494,17 @@ def _process_all_commands(local_vars: t.Any, console: t.Any, options: t.Any) ->
|
|
|
1442
1494
|
handle_config_updates(options)
|
|
1443
1495
|
return False
|
|
1444
1496
|
|
|
1497
|
+
# Handle semantic search commands early (they exit after execution)
|
|
1498
|
+
if not _handle_semantic_commands(
|
|
1499
|
+
local_vars["index"],
|
|
1500
|
+
local_vars["search"],
|
|
1501
|
+
local_vars["semantic_stats"],
|
|
1502
|
+
local_vars["remove_from_index"],
|
|
1503
|
+
console,
|
|
1504
|
+
options,
|
|
1505
|
+
):
|
|
1506
|
+
return False
|
|
1507
|
+
|
|
1445
1508
|
# Handle server commands (monitoring, websocket, MCP, zuban LSP)
|
|
1446
1509
|
if _handle_server_commands(
|
|
1447
1510
|
local_vars["monitor"],
|
|
@@ -1625,6 +1688,64 @@ def _handle_coverage_status(
|
|
|
1625
1688
|
return False
|
|
1626
1689
|
|
|
1627
1690
|
|
|
1691
|
+
def _handle_semantic_commands(
|
|
1692
|
+
index: str | None,
|
|
1693
|
+
search: str | None,
|
|
1694
|
+
semantic_stats: bool,
|
|
1695
|
+
remove_from_index: str | None,
|
|
1696
|
+
console: Console,
|
|
1697
|
+
options: t.Any,
|
|
1698
|
+
) -> bool:
|
|
1699
|
+
"""Handle semantic search commands.
|
|
1700
|
+
|
|
1701
|
+
Returns True if execution should continue, False if should return early.
|
|
1702
|
+
"""
|
|
1703
|
+
if not _has_semantic_operations(index, search, semantic_stats, remove_from_index):
|
|
1704
|
+
return True
|
|
1705
|
+
|
|
1706
|
+
console.print("[cyan]🔍[/cyan] Running semantic search operations...")
|
|
1707
|
+
|
|
1708
|
+
try:
|
|
1709
|
+
_execute_semantic_operations(index, search, semantic_stats, remove_from_index)
|
|
1710
|
+
return False # Exit after semantic operations
|
|
1711
|
+
|
|
1712
|
+
except Exception as e:
|
|
1713
|
+
console.print(f"[red]❌[/red] Semantic search error: {e}")
|
|
1714
|
+
return False
|
|
1715
|
+
|
|
1716
|
+
|
|
1717
|
+
def _has_semantic_operations(
|
|
1718
|
+
index: str | None,
|
|
1719
|
+
search: str | None,
|
|
1720
|
+
semantic_stats: bool,
|
|
1721
|
+
remove_from_index: str | None,
|
|
1722
|
+
) -> bool:
|
|
1723
|
+
"""Check if any semantic operations are requested."""
|
|
1724
|
+
return any([index, search, semantic_stats, remove_from_index])
|
|
1725
|
+
|
|
1726
|
+
|
|
1727
|
+
def _execute_semantic_operations(
|
|
1728
|
+
index: str | None,
|
|
1729
|
+
search: str | None,
|
|
1730
|
+
semantic_stats: bool,
|
|
1731
|
+
remove_from_index: str | None,
|
|
1732
|
+
) -> list[str]:
|
|
1733
|
+
"""Execute semantic operations in sequence and return remaining operations."""
|
|
1734
|
+
if index:
|
|
1735
|
+
handle_semantic_index(index)
|
|
1736
|
+
|
|
1737
|
+
if search:
|
|
1738
|
+
handle_semantic_search(search)
|
|
1739
|
+
|
|
1740
|
+
if semantic_stats:
|
|
1741
|
+
handle_semantic_stats()
|
|
1742
|
+
|
|
1743
|
+
if remove_from_index:
|
|
1744
|
+
handle_remove_from_semantic_index(remove_from_index)
|
|
1745
|
+
|
|
1746
|
+
return []
|
|
1747
|
+
|
|
1748
|
+
|
|
1628
1749
|
def _handle_enterprise_features(local_vars: t.Any, console: t.Any) -> bool:
|
|
1629
1750
|
"""Handle enterprise features."""
|
|
1630
1751
|
# Handle enterprise optimizer
|
crackerjack/agents/__init__.py
CHANGED
|
@@ -7,6 +7,7 @@ from . import (
|
|
|
7
7
|
performance_agent,
|
|
8
8
|
refactoring_agent,
|
|
9
9
|
security_agent,
|
|
10
|
+
semantic_agent,
|
|
10
11
|
test_creation_agent,
|
|
11
12
|
test_specialist_agent,
|
|
12
13
|
)
|
|
@@ -33,6 +34,7 @@ __all__ = [
|
|
|
33
34
|
"refactoring_agent",
|
|
34
35
|
"reset_agent_tracker",
|
|
35
36
|
"security_agent",
|
|
37
|
+
"semantic_agent",
|
|
36
38
|
"test_creation_agent",
|
|
37
39
|
"test_specialist_agent",
|
|
38
40
|
]
|
crackerjack/agents/base.py
CHANGED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bridge between crackerjack's built-in agents and Claude Code's external agents.
|
|
3
|
+
|
|
4
|
+
This module provides integration between the internal agent system and Claude Code's
|
|
5
|
+
specialized agents located in ~/.claude/agents. It enables crackerjack's built-in
|
|
6
|
+
agents to consult with expert external agents for complex scenarios.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import typing as t
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from .base import AgentContext, FixResult, Issue, IssueType
|
|
14
|
+
|
|
15
|
+
# Mapping of internal issue types to Claude Code external agents
|
|
16
|
+
CLAUDE_CODE_AGENT_MAPPING = {
|
|
17
|
+
IssueType.COMPLEXITY: ["refactoring-specialist", "crackerjack-architect"],
|
|
18
|
+
IssueType.DRY_VIOLATION: ["refactoring-specialist", "crackerjack-architect"],
|
|
19
|
+
IssueType.PERFORMANCE: ["performance-specialist", "python-pro"],
|
|
20
|
+
IssueType.SECURITY: ["security-auditor", "python-pro"],
|
|
21
|
+
IssueType.TYPE_ERROR: ["python-pro", "crackerjack-architect"],
|
|
22
|
+
IssueType.TEST_FAILURE: ["crackerjack-test-specialist", "python-pro"],
|
|
23
|
+
IssueType.TEST_ORGANIZATION: ["crackerjack-test-specialist", "testing-specialist"],
|
|
24
|
+
IssueType.IMPORT_ERROR: ["python-pro", "refactoring-specialist"],
|
|
25
|
+
IssueType.DOCUMENTATION: ["documentation-specialist", "crackerjack-architect"],
|
|
26
|
+
IssueType.FORMATTING: ["python-pro"],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Minimum confidence threshold for consulting external agents
|
|
30
|
+
EXTERNAL_CONSULTATION_THRESHOLD = 0.8
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ClaudeCodeBridge:
|
|
34
|
+
"""Bridge for consulting Claude Code external agents."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, context: AgentContext) -> None:
|
|
37
|
+
self.context = context
|
|
38
|
+
self.logger = logging.getLogger(__name__)
|
|
39
|
+
self._agent_path = Path.home() / ".claude" / "agents"
|
|
40
|
+
self._consultation_cache: dict[str, dict[str, t.Any]] = {}
|
|
41
|
+
|
|
42
|
+
def should_consult_external_agent(
|
|
43
|
+
self, issue: Issue, internal_confidence: float
|
|
44
|
+
) -> bool:
|
|
45
|
+
"""Determine if we should consult an external Claude Code agent."""
|
|
46
|
+
# Only consult for complex issues that meet threshold
|
|
47
|
+
if internal_confidence >= EXTERNAL_CONSULTATION_THRESHOLD:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
# Check if we have relevant external agents for this issue type
|
|
51
|
+
return issue.type in CLAUDE_CODE_AGENT_MAPPING
|
|
52
|
+
|
|
53
|
+
def _get_agent_mapping(self) -> dict[t.Any, list[str]]:
|
|
54
|
+
"""Get the agent mapping for external access."""
|
|
55
|
+
return CLAUDE_CODE_AGENT_MAPPING
|
|
56
|
+
|
|
57
|
+
def _get_consultation_threshold(self) -> float:
|
|
58
|
+
"""Get the consultation threshold for external access."""
|
|
59
|
+
return EXTERNAL_CONSULTATION_THRESHOLD
|
|
60
|
+
|
|
61
|
+
def get_recommended_external_agents(self, issue: Issue) -> list[str]:
|
|
62
|
+
"""Get list of recommended external agents for an issue."""
|
|
63
|
+
return CLAUDE_CODE_AGENT_MAPPING.get(issue.type, [])
|
|
64
|
+
|
|
65
|
+
def verify_agent_availability(self, agent_name: str) -> bool:
|
|
66
|
+
"""Check if a Claude Code agent file exists."""
|
|
67
|
+
agent_file = self._agent_path / f"{agent_name}.md"
|
|
68
|
+
return agent_file.exists()
|
|
69
|
+
|
|
70
|
+
async def consult_external_agent(
|
|
71
|
+
self, issue: Issue, agent_name: str, context: dict[str, t.Any] | None = None
|
|
72
|
+
) -> dict[str, t.Any]:
|
|
73
|
+
"""
|
|
74
|
+
Consult with a Claude Code external agent for expert guidance.
|
|
75
|
+
|
|
76
|
+
This method would ideally use the Task tool to invoke external agents,
|
|
77
|
+
but since we're within crackerjack's internal system, we'll simulate
|
|
78
|
+
the consultation process and provide structured recommendations.
|
|
79
|
+
"""
|
|
80
|
+
cache_key = (
|
|
81
|
+
f"{agent_name}:{issue.type.value}:{issue.file_path}:{issue.line_number}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if cache_key in self._consultation_cache:
|
|
85
|
+
self.logger.debug(f"Using cached consultation for {agent_name}")
|
|
86
|
+
return self._consultation_cache[cache_key]
|
|
87
|
+
|
|
88
|
+
if not self.verify_agent_availability(agent_name):
|
|
89
|
+
self.logger.warning(f"Agent {agent_name} not available in ~/.claude/agents")
|
|
90
|
+
return {"status": "unavailable", "recommendations": []}
|
|
91
|
+
|
|
92
|
+
# Generate consultation based on agent expertise and issue type
|
|
93
|
+
consultation = await self._generate_agent_consultation(
|
|
94
|
+
issue, agent_name, context
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Cache successful consultations
|
|
98
|
+
if consultation.get("status") == "success":
|
|
99
|
+
self._consultation_cache[cache_key] = consultation
|
|
100
|
+
|
|
101
|
+
return consultation
|
|
102
|
+
|
|
103
|
+
async def _generate_agent_consultation(
|
|
104
|
+
self, issue: Issue, agent_name: str, context: dict[str, t.Any] | None = None
|
|
105
|
+
) -> dict[str, t.Any]:
|
|
106
|
+
"""Generate structured consultation response from agent expertise."""
|
|
107
|
+
consultation: dict[str, t.Any] = {
|
|
108
|
+
"status": "success",
|
|
109
|
+
"agent": agent_name,
|
|
110
|
+
"issue_type": issue.type.value,
|
|
111
|
+
"recommendations": [],
|
|
112
|
+
"patterns": [],
|
|
113
|
+
"validation_steps": [],
|
|
114
|
+
"confidence": 0.9,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Agent-specific consultation logic
|
|
118
|
+
if agent_name == "crackerjack-architect":
|
|
119
|
+
consultation.update(
|
|
120
|
+
await self._consult_crackerjack_architect(issue, context)
|
|
121
|
+
)
|
|
122
|
+
elif agent_name == "python-pro":
|
|
123
|
+
consultation.update(await self._consult_python_pro(issue, context))
|
|
124
|
+
elif agent_name == "security-auditor":
|
|
125
|
+
consultation.update(await self._consult_security_auditor(issue, context))
|
|
126
|
+
elif agent_name == "refactoring-specialist":
|
|
127
|
+
consultation.update(
|
|
128
|
+
await self._consult_refactoring_specialist(issue, context)
|
|
129
|
+
)
|
|
130
|
+
elif agent_name == "crackerjack-test-specialist":
|
|
131
|
+
consultation.update(await self._consult_test_specialist(issue, context))
|
|
132
|
+
else:
|
|
133
|
+
consultation.update(
|
|
134
|
+
await self._consult_generic_agent(issue, agent_name, context)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return consultation
|
|
138
|
+
|
|
139
|
+
async def _consult_crackerjack_architect(
|
|
140
|
+
self, issue: Issue, context: dict[str, t.Any] | None = None
|
|
141
|
+
) -> dict[str, t.Any]:
|
|
142
|
+
"""Consult with crackerjack-architect for architectural guidance."""
|
|
143
|
+
return {
|
|
144
|
+
"recommendations": [
|
|
145
|
+
"Apply clean code principles (DRY, YAGNI, KISS)",
|
|
146
|
+
"Follow crackerjack's modular architecture patterns",
|
|
147
|
+
"Use protocol-based dependency injection",
|
|
148
|
+
"Break complex functions into focused helper methods",
|
|
149
|
+
"Maintain single responsibility principle",
|
|
150
|
+
],
|
|
151
|
+
"patterns": [
|
|
152
|
+
"extract_method",
|
|
153
|
+
"dependency_injection",
|
|
154
|
+
"protocol_interfaces",
|
|
155
|
+
"helper_methods",
|
|
156
|
+
"single_responsibility",
|
|
157
|
+
],
|
|
158
|
+
"validation_steps": [
|
|
159
|
+
"run_complexity_check",
|
|
160
|
+
"verify_type_annotations",
|
|
161
|
+
"check_architectural_consistency",
|
|
162
|
+
"validate_against_crackerjack_patterns",
|
|
163
|
+
],
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async def _consult_python_pro(
|
|
167
|
+
self, issue: Issue, context: dict[str, t.Any] | None = None
|
|
168
|
+
) -> dict[str, t.Any]:
|
|
169
|
+
"""Consult with python-pro for Python-specific best practices."""
|
|
170
|
+
return {
|
|
171
|
+
"recommendations": [
|
|
172
|
+
"Use modern Python 3.13+ type hints with | unions",
|
|
173
|
+
"Apply proper error handling patterns",
|
|
174
|
+
"Follow PEP 8 style guidelines",
|
|
175
|
+
"Use pathlib over os.path",
|
|
176
|
+
"Implement proper context managers",
|
|
177
|
+
],
|
|
178
|
+
"patterns": [
|
|
179
|
+
"type_annotations",
|
|
180
|
+
"context_managers",
|
|
181
|
+
"exception_handling",
|
|
182
|
+
"python_idioms",
|
|
183
|
+
"modern_syntax",
|
|
184
|
+
],
|
|
185
|
+
"validation_steps": [
|
|
186
|
+
"run_type_checking",
|
|
187
|
+
"verify_python_compatibility",
|
|
188
|
+
"check_style_compliance",
|
|
189
|
+
"validate_error_handling",
|
|
190
|
+
],
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async def _consult_security_auditor(
|
|
194
|
+
self, issue: Issue, context: dict[str, t.Any] | None = None
|
|
195
|
+
) -> dict[str, t.Any]:
|
|
196
|
+
"""Consult with security-auditor for security best practices."""
|
|
197
|
+
return {
|
|
198
|
+
"recommendations": [
|
|
199
|
+
"Never use hardcoded paths or credentials",
|
|
200
|
+
"Use secure temp file creation",
|
|
201
|
+
"Avoid shell=True in subprocess calls",
|
|
202
|
+
"Implement proper input validation",
|
|
203
|
+
"Use environment variables for sensitive data",
|
|
204
|
+
],
|
|
205
|
+
"patterns": [
|
|
206
|
+
"secure_temp_files",
|
|
207
|
+
"input_validation",
|
|
208
|
+
"safe_subprocess",
|
|
209
|
+
"environment_variables",
|
|
210
|
+
"sanitization",
|
|
211
|
+
],
|
|
212
|
+
"validation_steps": [
|
|
213
|
+
"run_security_scan",
|
|
214
|
+
"check_for_hardcoded_secrets",
|
|
215
|
+
"validate_subprocess_calls",
|
|
216
|
+
"verify_temp_file_handling",
|
|
217
|
+
],
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async def _consult_refactoring_specialist(
|
|
221
|
+
self, issue: Issue, context: dict[str, t.Any] | None = None
|
|
222
|
+
) -> dict[str, t.Any]:
|
|
223
|
+
"""Consult with refactoring-specialist for code improvement."""
|
|
224
|
+
return {
|
|
225
|
+
"recommendations": [
|
|
226
|
+
"Break down complex functions (complexity ≤ 15)",
|
|
227
|
+
"Extract common patterns into utilities",
|
|
228
|
+
"Remove dead code and unused imports",
|
|
229
|
+
"Apply DRY principle to eliminate duplication",
|
|
230
|
+
"Use composition over inheritance",
|
|
231
|
+
],
|
|
232
|
+
"patterns": [
|
|
233
|
+
"extract_method",
|
|
234
|
+
"eliminate_duplication",
|
|
235
|
+
"dead_code_removal",
|
|
236
|
+
"complexity_reduction",
|
|
237
|
+
"composition_pattern",
|
|
238
|
+
],
|
|
239
|
+
"validation_steps": [
|
|
240
|
+
"measure_complexity_reduction",
|
|
241
|
+
"verify_test_coverage_maintained",
|
|
242
|
+
"check_for_dead_code",
|
|
243
|
+
"validate_duplication_removal",
|
|
244
|
+
],
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async def _consult_test_specialist(
|
|
248
|
+
self, issue: Issue, context: dict[str, t.Any] | None = None
|
|
249
|
+
) -> dict[str, t.Any]:
|
|
250
|
+
"""Consult with crackerjack-test-specialist for testing guidance."""
|
|
251
|
+
return {
|
|
252
|
+
"recommendations": [
|
|
253
|
+
"Avoid complex async tests that can hang",
|
|
254
|
+
"Use synchronous config tests for reliability",
|
|
255
|
+
"Mock external dependencies properly",
|
|
256
|
+
"Follow crackerjack's testing patterns",
|
|
257
|
+
"Maintain test coverage ratchet",
|
|
258
|
+
],
|
|
259
|
+
"patterns": [
|
|
260
|
+
"synchronous_tests",
|
|
261
|
+
"proper_mocking",
|
|
262
|
+
"test_organization",
|
|
263
|
+
"coverage_improvement",
|
|
264
|
+
"fixture_patterns",
|
|
265
|
+
],
|
|
266
|
+
"validation_steps": [
|
|
267
|
+
"run_test_suite",
|
|
268
|
+
"verify_coverage_increase",
|
|
269
|
+
"check_test_reliability",
|
|
270
|
+
"validate_mock_usage",
|
|
271
|
+
],
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async def _consult_generic_agent(
|
|
275
|
+
self, issue: Issue, agent_name: str, context: dict[str, t.Any] | None = None
|
|
276
|
+
) -> dict[str, t.Any]:
|
|
277
|
+
"""Generic consultation for unspecified agents."""
|
|
278
|
+
return {
|
|
279
|
+
"recommendations": [
|
|
280
|
+
f"Consult {agent_name} documentation for specific guidance",
|
|
281
|
+
"Apply domain-specific best practices",
|
|
282
|
+
"Follow established patterns and conventions",
|
|
283
|
+
],
|
|
284
|
+
"patterns": ["domain_specific_patterns"],
|
|
285
|
+
"validation_steps": ["validate_domain_requirements"],
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
def create_enhanced_fix_result(
|
|
289
|
+
self, base_result: FixResult, consultations: list[dict[str, t.Any]]
|
|
290
|
+
) -> FixResult:
|
|
291
|
+
"""Enhance a FixResult with external agent consultations."""
|
|
292
|
+
enhanced_result = FixResult(
|
|
293
|
+
success=base_result.success,
|
|
294
|
+
confidence=base_result.confidence,
|
|
295
|
+
fixes_applied=base_result.fixes_applied.copy(),
|
|
296
|
+
remaining_issues=base_result.remaining_issues.copy(),
|
|
297
|
+
recommendations=base_result.recommendations.copy(),
|
|
298
|
+
files_modified=base_result.files_modified.copy(),
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Aggregate recommendations from all consultations
|
|
302
|
+
for consultation in consultations:
|
|
303
|
+
if consultation.get("status") == "success":
|
|
304
|
+
agent_name = consultation.get("agent", "unknown")
|
|
305
|
+
enhanced_result.recommendations.extend(
|
|
306
|
+
[
|
|
307
|
+
f"[{agent_name}] {rec}"
|
|
308
|
+
for rec in consultation.get("recommendations", [])
|
|
309
|
+
]
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Boost confidence if external agents provided guidance
|
|
313
|
+
external_confidence = consultation.get("confidence", 0.0)
|
|
314
|
+
enhanced_result.confidence = max(
|
|
315
|
+
enhanced_result.confidence,
|
|
316
|
+
(enhanced_result.confidence + external_confidence) / 2,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
return enhanced_result
|
|
@@ -450,12 +450,15 @@ class AgentCoordinator:
|
|
|
450
450
|
|
|
451
451
|
try:
|
|
452
452
|
plan = await architect.plan_before_action(primary_issue)
|
|
453
|
-
plan
|
|
453
|
+
# Ensure plan is properly typed as dict[str, Any]
|
|
454
|
+
if not isinstance(plan, dict):
|
|
455
|
+
plan = {"strategy": "default", "confidence": 0.5}
|
|
456
|
+
enriched_plan = self._enrich_architectural_plan(plan, all_issues)
|
|
454
457
|
|
|
455
458
|
self.logger.info(
|
|
456
|
-
f"Created architectural plan: {
|
|
459
|
+
f"Created architectural plan: {enriched_plan.get('strategy', 'unknown')}"
|
|
457
460
|
)
|
|
458
|
-
return
|
|
461
|
+
return enriched_plan
|
|
459
462
|
|
|
460
463
|
except Exception as e:
|
|
461
464
|
self.logger.exception(f"Failed to create architectural plan: {e}")
|