crackerjack 0.33.0__py3-none-any.whl โ 0.33.1__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 +1350 -34
- crackerjack/adapters/__init__.py +17 -0
- crackerjack/adapters/lsp_client.py +358 -0
- crackerjack/adapters/rust_tool_adapter.py +194 -0
- crackerjack/adapters/rust_tool_manager.py +193 -0
- crackerjack/adapters/skylos_adapter.py +231 -0
- crackerjack/adapters/zuban_adapter.py +560 -0
- crackerjack/agents/base.py +7 -3
- crackerjack/agents/coordinator.py +271 -33
- crackerjack/agents/documentation_agent.py +9 -15
- crackerjack/agents/dry_agent.py +3 -15
- crackerjack/agents/formatting_agent.py +1 -1
- crackerjack/agents/import_optimization_agent.py +36 -180
- crackerjack/agents/performance_agent.py +17 -98
- crackerjack/agents/performance_helpers.py +7 -31
- crackerjack/agents/proactive_agent.py +1 -3
- crackerjack/agents/refactoring_agent.py +16 -85
- crackerjack/agents/refactoring_helpers.py +7 -42
- crackerjack/agents/security_agent.py +9 -48
- crackerjack/agents/test_creation_agent.py +356 -513
- crackerjack/agents/test_specialist_agent.py +0 -4
- crackerjack/api.py +6 -25
- crackerjack/cli/cache_handlers.py +204 -0
- crackerjack/cli/cache_handlers_enhanced.py +683 -0
- crackerjack/cli/facade.py +100 -0
- crackerjack/cli/handlers.py +224 -9
- crackerjack/cli/interactive.py +6 -4
- crackerjack/cli/options.py +642 -55
- crackerjack/cli/utils.py +2 -1
- crackerjack/code_cleaner.py +58 -117
- crackerjack/config/global_lock_config.py +8 -48
- crackerjack/config/hooks.py +53 -62
- crackerjack/core/async_workflow_orchestrator.py +24 -34
- crackerjack/core/autofix_coordinator.py +3 -17
- crackerjack/core/enhanced_container.py +4 -13
- crackerjack/core/file_lifecycle.py +12 -89
- crackerjack/core/performance.py +2 -2
- crackerjack/core/performance_monitor.py +15 -55
- crackerjack/core/phase_coordinator.py +104 -204
- crackerjack/core/resource_manager.py +14 -90
- crackerjack/core/service_watchdog.py +62 -95
- crackerjack/core/session_coordinator.py +149 -0
- crackerjack/core/timeout_manager.py +14 -72
- crackerjack/core/websocket_lifecycle.py +13 -78
- crackerjack/core/workflow_orchestrator.py +171 -174
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +765 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +977 -0
- crackerjack/dynamic_config.py +55 -50
- crackerjack/executors/async_hook_executor.py +10 -15
- crackerjack/executors/cached_hook_executor.py +117 -43
- crackerjack/executors/hook_executor.py +8 -34
- crackerjack/executors/hook_lock_manager.py +26 -183
- crackerjack/executors/individual_hook_executor.py +13 -11
- crackerjack/executors/lsp_aware_hook_executor.py +270 -0
- crackerjack/executors/tool_proxy.py +417 -0
- crackerjack/hooks/lsp_hook.py +79 -0
- crackerjack/intelligence/adaptive_learning.py +25 -10
- crackerjack/intelligence/agent_orchestrator.py +2 -5
- crackerjack/intelligence/agent_registry.py +34 -24
- crackerjack/intelligence/agent_selector.py +5 -7
- crackerjack/interactive.py +17 -6
- crackerjack/managers/async_hook_manager.py +0 -1
- crackerjack/managers/hook_manager.py +79 -1
- crackerjack/managers/publish_manager.py +44 -8
- crackerjack/managers/test_command_builder.py +1 -15
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +98 -7
- crackerjack/managers/test_manager_backup.py +10 -9
- crackerjack/mcp/cache.py +2 -2
- crackerjack/mcp/client_runner.py +1 -1
- crackerjack/mcp/context.py +191 -68
- crackerjack/mcp/dashboard.py +7 -5
- crackerjack/mcp/enhanced_progress_monitor.py +31 -28
- crackerjack/mcp/file_monitor.py +30 -23
- crackerjack/mcp/progress_components.py +31 -21
- crackerjack/mcp/progress_monitor.py +50 -53
- crackerjack/mcp/rate_limiter.py +6 -6
- crackerjack/mcp/server_core.py +17 -16
- crackerjack/mcp/service_watchdog.py +2 -1
- crackerjack/mcp/state.py +4 -7
- crackerjack/mcp/task_manager.py +11 -9
- crackerjack/mcp/tools/core_tools.py +173 -32
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +8 -10
- crackerjack/mcp/tools/execution_tools_backup.py +42 -30
- crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
- crackerjack/mcp/tools/intelligence_tools.py +5 -2
- crackerjack/mcp/tools/monitoring_tools.py +33 -70
- crackerjack/mcp/tools/proactive_tools.py +24 -11
- crackerjack/mcp/tools/progress_tools.py +5 -8
- crackerjack/mcp/tools/utility_tools.py +20 -14
- crackerjack/mcp/tools/workflow_executor.py +62 -40
- crackerjack/mcp/websocket/app.py +8 -0
- crackerjack/mcp/websocket/endpoints.py +352 -357
- crackerjack/mcp/websocket/jobs.py +40 -57
- crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
- crackerjack/mcp/websocket/server.py +7 -25
- crackerjack/mcp/websocket/websocket_handler.py +6 -17
- crackerjack/mixins/__init__.py +0 -2
- crackerjack/mixins/error_handling.py +1 -70
- crackerjack/models/config.py +12 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +122 -122
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/monitoring/ai_agent_watchdog.py +13 -13
- crackerjack/monitoring/metrics_collector.py +426 -0
- crackerjack/monitoring/regression_prevention.py +8 -8
- crackerjack/monitoring/websocket_server.py +643 -0
- crackerjack/orchestration/advanced_orchestrator.py +11 -6
- crackerjack/orchestration/coverage_improvement.py +3 -3
- crackerjack/orchestration/execution_strategies.py +26 -6
- crackerjack/orchestration/test_progress_streamer.py +8 -5
- crackerjack/plugins/base.py +2 -2
- crackerjack/plugins/hooks.py +7 -0
- crackerjack/plugins/managers.py +11 -8
- crackerjack/security/__init__.py +0 -1
- crackerjack/security/audit.py +6 -35
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +615 -0
- crackerjack/services/backup_service.py +2 -2
- crackerjack/services/bounded_status_operations.py +15 -152
- crackerjack/services/cache.py +127 -1
- crackerjack/services/changelog_automation.py +395 -0
- crackerjack/services/config.py +15 -9
- crackerjack/services/config_merge.py +19 -80
- crackerjack/services/config_template.py +506 -0
- crackerjack/services/contextual_ai_assistant.py +48 -22
- crackerjack/services/coverage_badge_service.py +171 -0
- crackerjack/services/coverage_ratchet.py +27 -25
- crackerjack/services/debug.py +3 -3
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +14 -11
- crackerjack/services/documentation_generator.py +491 -0
- crackerjack/services/documentation_service.py +675 -0
- crackerjack/services/enhanced_filesystem.py +6 -5
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/git.py +8 -25
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +11 -30
- crackerjack/services/input_validator.py +5 -97
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +15 -12
- crackerjack/services/logging.py +4 -3
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +19 -87
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +9 -67
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +18 -59
- crackerjack/services/performance_cache.py +20 -81
- crackerjack/services/performance_monitor.py +27 -95
- crackerjack/services/predictive_analytics.py +510 -0
- crackerjack/services/quality_baseline.py +234 -0
- crackerjack/services/quality_baseline_enhanced.py +646 -0
- crackerjack/services/quality_intelligence.py +785 -0
- crackerjack/services/regex_patterns.py +605 -524
- crackerjack/services/regex_utils.py +43 -123
- crackerjack/services/secure_path_utils.py +5 -164
- crackerjack/services/secure_status_formatter.py +30 -141
- crackerjack/services/secure_subprocess.py +11 -92
- crackerjack/services/security.py +9 -41
- crackerjack/services/security_logger.py +12 -24
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/thread_safe_status_collector.py +19 -125
- crackerjack/services/unified_config.py +21 -13
- crackerjack/services/validation_rate_limiter.py +5 -54
- crackerjack/services/version_analyzer.py +459 -0
- crackerjack/services/version_checker.py +1 -1
- crackerjack/services/websocket_resource_limiter.py +10 -144
- crackerjack/services/zuban_lsp_service.py +390 -0
- crackerjack/slash_commands/__init__.py +2 -7
- crackerjack/slash_commands/run.md +2 -2
- crackerjack/tools/validate_input_validator_patterns.py +14 -40
- crackerjack/tools/validate_regex_patterns.py +19 -48
- {crackerjack-0.33.0.dist-info โ crackerjack-0.33.1.dist-info}/METADATA +196 -25
- crackerjack-0.33.1.dist-info/RECORD +229 -0
- crackerjack/CLAUDE.md +0 -207
- crackerjack/RULES.md +0 -380
- crackerjack/py313.py +0 -234
- crackerjack-0.33.0.dist-info/RECORD +0 -187
- {crackerjack-0.33.0.dist-info โ crackerjack-0.33.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.33.0.dist-info โ crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.33.0.dist-info โ crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import typing as t
|
|
2
2
|
|
|
3
|
-
from crackerjack.mcp.context import get_context
|
|
3
|
+
from crackerjack.mcp.context import MCPServerContext, get_context
|
|
4
|
+
from crackerjack.mcp.rate_limiter import RateLimitMiddleware
|
|
4
5
|
from crackerjack.services.input_validator import (
|
|
6
|
+
SecureInputValidator,
|
|
5
7
|
get_input_validator,
|
|
6
8
|
)
|
|
7
9
|
|
|
10
|
+
if t.TYPE_CHECKING:
|
|
11
|
+
from crackerjack.core.workflow_orchestrator import WorkflowOrchestrator
|
|
12
|
+
from crackerjack.models.config import WorkflowOptions
|
|
13
|
+
|
|
8
14
|
|
|
9
15
|
async def create_task_with_subagent(
|
|
10
16
|
description: str,
|
|
@@ -12,10 +18,8 @@ async def create_task_with_subagent(
|
|
|
12
18
|
subagent_type: str,
|
|
13
19
|
) -> dict[str, t.Any]:
|
|
14
20
|
try:
|
|
15
|
-
# Input validation with security checks
|
|
16
21
|
validator = get_input_validator()
|
|
17
22
|
|
|
18
|
-
# Validate description
|
|
19
23
|
desc_result = validator.validate_command_args(description)
|
|
20
24
|
if not desc_result.valid:
|
|
21
25
|
return {
|
|
@@ -24,7 +28,6 @@ async def create_task_with_subagent(
|
|
|
24
28
|
"validation_type": desc_result.validation_type,
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
# Validate prompt
|
|
28
31
|
prompt_result = validator.validate_command_args(prompt)
|
|
29
32
|
if not prompt_result.valid:
|
|
30
33
|
return {
|
|
@@ -33,7 +36,6 @@ async def create_task_with_subagent(
|
|
|
33
36
|
"validation_type": prompt_result.validation_type,
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
# Validate subagent_type (should be safe identifier)
|
|
37
39
|
subagent_result = validator.sanitizer.sanitize_string(
|
|
38
40
|
subagent_type, max_length=100, strict_alphanumeric=True
|
|
39
41
|
)
|
|
@@ -44,7 +46,6 @@ async def create_task_with_subagent(
|
|
|
44
46
|
"validation_type": subagent_result.validation_type,
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
# Use sanitized values
|
|
48
49
|
sanitized_description = desc_result.sanitized_value or description
|
|
49
50
|
sanitized_prompt = prompt_result.sanitized_value or prompt
|
|
50
51
|
sanitized_subagent = subagent_result.sanitized_value
|
|
@@ -72,7 +73,9 @@ async def create_task_with_subagent(
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
async def _validate_stage_request(
|
|
76
|
+
async def _validate_stage_request(
|
|
77
|
+
context: MCPServerContext | None, rate_limiter: RateLimitMiddleware | None
|
|
78
|
+
) -> str | None:
|
|
76
79
|
if not context:
|
|
77
80
|
return '{"error": "Server context not available", "success": false}'
|
|
78
81
|
|
|
@@ -83,17 +86,15 @@ async def _validate_stage_request(context, rate_limiter) -> str | None:
|
|
|
83
86
|
return None
|
|
84
87
|
|
|
85
88
|
|
|
86
|
-
def _parse_stage_args(args: str, kwargs: str) -> tuple[str, dict] | str:
|
|
89
|
+
def _parse_stage_args(args: str, kwargs: str) -> tuple[str, dict[str, t.Any]] | str:
|
|
87
90
|
try:
|
|
88
91
|
validator = get_input_validator()
|
|
89
92
|
|
|
90
|
-
# Validate stage argument
|
|
91
93
|
stage_validation = _validate_stage_argument(validator, args)
|
|
92
94
|
if isinstance(stage_validation, str):
|
|
93
95
|
return stage_validation
|
|
94
96
|
stage = stage_validation
|
|
95
97
|
|
|
96
|
-
# Validate and parse kwargs
|
|
97
98
|
kwargs_validation = _validate_kwargs_argument(validator, kwargs)
|
|
98
99
|
if isinstance(kwargs_validation, str):
|
|
99
100
|
return kwargs_validation
|
|
@@ -105,15 +106,14 @@ def _parse_stage_args(args: str, kwargs: str) -> tuple[str, dict] | str:
|
|
|
105
106
|
return f'{{"error": "Stage argument parsing failed: {e}", "success": false}}'
|
|
106
107
|
|
|
107
108
|
|
|
108
|
-
def _validate_stage_argument(validator, args: str) -> str:
|
|
109
|
-
"""Validate and sanitize the stage argument."""
|
|
109
|
+
def _validate_stage_argument(validator: SecureInputValidator, args: str) -> str:
|
|
110
110
|
stage_result = validator.sanitizer.sanitize_string(
|
|
111
111
|
args.strip(), max_length=50, strict_alphanumeric=True
|
|
112
112
|
)
|
|
113
113
|
if not stage_result.valid:
|
|
114
114
|
return f'{{"error": "Invalid stage argument: {stage_result.error_message}", "success": false}}'
|
|
115
115
|
|
|
116
|
-
stage = stage_result.sanitized_value.lower()
|
|
116
|
+
stage: str = str(stage_result.sanitized_value).lower()
|
|
117
117
|
valid_stages = {"fast", "comprehensive", "tests", "cleaning", "init"}
|
|
118
118
|
|
|
119
119
|
if stage not in valid_stages:
|
|
@@ -122,9 +122,10 @@ def _validate_stage_argument(validator, args: str) -> str:
|
|
|
122
122
|
return stage
|
|
123
123
|
|
|
124
124
|
|
|
125
|
-
def _validate_kwargs_argument(
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
def _validate_kwargs_argument(
|
|
126
|
+
validator: SecureInputValidator, kwargs: str
|
|
127
|
+
) -> dict[str, t.Any] | str:
|
|
128
|
+
extra_kwargs: dict[str, t.Any] = {}
|
|
128
129
|
if not kwargs.strip():
|
|
129
130
|
return extra_kwargs
|
|
130
131
|
|
|
@@ -134,7 +135,6 @@ def _validate_kwargs_argument(validator, kwargs: str) -> dict | str:
|
|
|
134
135
|
|
|
135
136
|
extra_kwargs = kwargs_result.sanitized_value
|
|
136
137
|
|
|
137
|
-
# Additional validation on JSON structure
|
|
138
138
|
if not isinstance(extra_kwargs, dict):
|
|
139
139
|
return f'{{"error": "kwargs must be a JSON object, got {type(extra_kwargs).__name__}", "success": false}}'
|
|
140
140
|
|
|
@@ -146,31 +146,171 @@ def _configure_stage_options(stage: str) -> "WorkflowOptions":
|
|
|
146
146
|
|
|
147
147
|
options = WorkflowOptions()
|
|
148
148
|
if stage in {"fast", "comprehensive"}:
|
|
149
|
-
options.skip_hooks = False
|
|
149
|
+
options.hooks.skip_hooks = False
|
|
150
150
|
elif stage == "tests":
|
|
151
151
|
options.testing.test = True
|
|
152
152
|
elif stage == "cleaning":
|
|
153
153
|
options.cleaning.clean = True
|
|
154
154
|
elif stage == "init":
|
|
155
|
-
options.skip_hooks = True
|
|
155
|
+
options.hooks.skip_hooks = True
|
|
156
156
|
return options
|
|
157
157
|
|
|
158
158
|
|
|
159
|
-
def _execute_stage(
|
|
159
|
+
def _execute_stage(
|
|
160
|
+
orchestrator: "WorkflowOrchestrator", stage: str, options: "WorkflowOptions"
|
|
161
|
+
) -> bool:
|
|
162
|
+
# Convert WorkflowOptions to OptionsProtocol
|
|
163
|
+
adapted_options = _adapt_workflow_options_to_protocol(options)
|
|
164
|
+
|
|
160
165
|
if stage == "fast":
|
|
161
|
-
return orchestrator.run_fast_hooks_only(
|
|
166
|
+
return orchestrator.run_fast_hooks_only(adapted_options)
|
|
162
167
|
if stage == "comprehensive":
|
|
163
|
-
return orchestrator.run_comprehensive_hooks_only(
|
|
168
|
+
return orchestrator.run_comprehensive_hooks_only(adapted_options)
|
|
164
169
|
if stage == "tests":
|
|
165
|
-
return orchestrator.run_testing_phase(
|
|
170
|
+
return orchestrator.run_testing_phase(adapted_options)
|
|
166
171
|
if stage == "cleaning":
|
|
167
|
-
return orchestrator.run_cleaning_phase(
|
|
168
|
-
if stage == "init":
|
|
169
|
-
return _execute_init_stage(orchestrator)
|
|
172
|
+
return orchestrator.run_cleaning_phase(adapted_options)
|
|
170
173
|
return False
|
|
171
174
|
|
|
172
175
|
|
|
173
|
-
def
|
|
176
|
+
def _adapt_workflow_options_to_protocol(options: "WorkflowOptions") -> t.Any:
|
|
177
|
+
"""Adapt WorkflowOptions to match OptionsProtocol."""
|
|
178
|
+
return _AdaptedOptions(options) # type: ignore
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class _AdaptedOptions:
|
|
182
|
+
"""Adapter class to convert WorkflowOptions to OptionsProtocol."""
|
|
183
|
+
|
|
184
|
+
def __init__(self, opts: "WorkflowOptions"):
|
|
185
|
+
self.opts = opts
|
|
186
|
+
|
|
187
|
+
# Git properties
|
|
188
|
+
@property
|
|
189
|
+
def commit(self) -> bool:
|
|
190
|
+
return getattr(self.opts.git, "commit", False)
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def create_pr(self) -> bool:
|
|
194
|
+
return getattr(self.opts.git, "create_pr", False)
|
|
195
|
+
|
|
196
|
+
# Execution properties
|
|
197
|
+
@property
|
|
198
|
+
def interactive(self) -> bool:
|
|
199
|
+
return getattr(self.opts.execution, "interactive", False)
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def no_config_updates(self) -> bool:
|
|
203
|
+
return getattr(self.opts.execution, "no_config_updates", False)
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def verbose(self) -> bool:
|
|
207
|
+
return getattr(self.opts.execution, "verbose", False)
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def async_mode(self) -> bool:
|
|
211
|
+
return getattr(self.opts.execution, "async_mode", False)
|
|
212
|
+
|
|
213
|
+
# Testing properties
|
|
214
|
+
@property
|
|
215
|
+
def test(self) -> bool:
|
|
216
|
+
return getattr(self.opts.testing, "test", False)
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def benchmark(self) -> bool:
|
|
220
|
+
return getattr(self.opts.testing, "benchmark", False)
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def test_workers(self) -> int:
|
|
224
|
+
return getattr(self.opts.testing, "test_workers", 0)
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def test_timeout(self) -> int:
|
|
228
|
+
return getattr(self.opts.testing, "test_timeout", 0)
|
|
229
|
+
|
|
230
|
+
# Publishing properties
|
|
231
|
+
@property
|
|
232
|
+
def publish(self) -> t.Any | None:
|
|
233
|
+
return getattr(self.opts.publishing, "publish", None)
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def bump(self) -> t.Any | None:
|
|
237
|
+
return getattr(self.opts.publishing, "bump", None)
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def all(self) -> t.Any | None:
|
|
241
|
+
return getattr(self.opts.publishing, "all", None)
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def no_git_tags(self) -> bool:
|
|
245
|
+
return getattr(self.opts.publishing, "no_git_tags", False)
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def skip_version_check(self) -> bool:
|
|
249
|
+
return getattr(self.opts.publishing, "skip_version_check", False)
|
|
250
|
+
|
|
251
|
+
# AI properties
|
|
252
|
+
@property
|
|
253
|
+
def ai_agent(self) -> bool:
|
|
254
|
+
return getattr(self.opts.ai, "ai_agent", False)
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def start_mcp_server(self) -> bool:
|
|
258
|
+
return getattr(self.opts.ai, "start_mcp_server", False)
|
|
259
|
+
|
|
260
|
+
# Hook properties
|
|
261
|
+
@property
|
|
262
|
+
def skip_hooks(self) -> bool:
|
|
263
|
+
return getattr(self.opts.hooks, "skip_hooks", False)
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def update_precommit(self) -> bool:
|
|
267
|
+
return getattr(self.opts.hooks, "update_precommit", False)
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def experimental_hooks(self) -> bool:
|
|
271
|
+
return getattr(self.opts.hooks, "experimental_hooks", False)
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
def enable_pyrefly(self) -> bool:
|
|
275
|
+
return getattr(self.opts.hooks, "enable_pyrefly", False)
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def enable_ty(self) -> bool:
|
|
279
|
+
return getattr(self.opts.hooks, "enable_ty", False)
|
|
280
|
+
|
|
281
|
+
# Cleaning properties
|
|
282
|
+
@property
|
|
283
|
+
def clean(self) -> bool:
|
|
284
|
+
return getattr(self.opts.cleaning, "clean", False)
|
|
285
|
+
|
|
286
|
+
# Progress properties
|
|
287
|
+
@property
|
|
288
|
+
def track_progress(self) -> bool:
|
|
289
|
+
return getattr(self.opts.progress, "track_progress", False)
|
|
290
|
+
|
|
291
|
+
# Default/static properties
|
|
292
|
+
@property
|
|
293
|
+
def cleanup(self) -> t.Any | None:
|
|
294
|
+
return None
|
|
295
|
+
|
|
296
|
+
@property
|
|
297
|
+
def cleanup_pypi(self) -> bool:
|
|
298
|
+
return False
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def coverage(self) -> bool:
|
|
302
|
+
return False
|
|
303
|
+
|
|
304
|
+
@property
|
|
305
|
+
def keep_releases(self) -> int:
|
|
306
|
+
return 10
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def fast(self) -> bool:
|
|
310
|
+
return False
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _execute_init_stage(orchestrator: "WorkflowOrchestrator") -> bool:
|
|
174
314
|
try:
|
|
175
315
|
from pathlib import Path
|
|
176
316
|
|
|
@@ -188,7 +328,8 @@ def _execute_init_stage(orchestrator) -> bool:
|
|
|
188
328
|
|
|
189
329
|
results = init_service.initialize_project_full(target_path=Path.cwd())
|
|
190
330
|
|
|
191
|
-
|
|
331
|
+
success_result: bool = bool(results.get("success", False))
|
|
332
|
+
return success_result
|
|
192
333
|
|
|
193
334
|
except Exception as e:
|
|
194
335
|
if hasattr(orchestrator, "console"):
|
|
@@ -197,7 +338,7 @@ def _execute_init_stage(orchestrator) -> bool:
|
|
|
197
338
|
|
|
198
339
|
|
|
199
340
|
def register_core_tools(mcp_app: t.Any) -> None:
|
|
200
|
-
@mcp_app.tool()
|
|
341
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
201
342
|
async def run_crackerjack_stage(args: str, kwargs: str) -> str:
|
|
202
343
|
context = get_context()
|
|
203
344
|
rate_limiter = context.rate_limiter if context else None
|
|
@@ -258,8 +399,8 @@ def _detect_errors_and_suggestions(
|
|
|
258
399
|
) -> tuple[list[str], list[str]]:
|
|
259
400
|
import re
|
|
260
401
|
|
|
261
|
-
detected_errors = []
|
|
262
|
-
suggestions = []
|
|
402
|
+
detected_errors: list[str] = []
|
|
403
|
+
suggestions: list[str] = []
|
|
263
404
|
|
|
264
405
|
for error_type, pattern in _get_error_patterns():
|
|
265
406
|
if re.search(pattern, text, re.IGNORECASE):
|
|
@@ -271,7 +412,7 @@ def _detect_errors_and_suggestions(
|
|
|
271
412
|
|
|
272
413
|
|
|
273
414
|
def register_analyze_errors_tool(mcp_app: t.Any) -> None:
|
|
274
|
-
@mcp_app.tool()
|
|
415
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
275
416
|
async def analyze_errors(output: str = "", include_suggestions: bool = True) -> str:
|
|
276
417
|
context = get_context()
|
|
277
418
|
if not context:
|
|
@@ -25,7 +25,8 @@ def _get_cached_patterns(context: t.Any, use_cache: bool) -> list[t.Any]:
|
|
|
25
25
|
with suppress(Exception):
|
|
26
26
|
cache = getattr(context, "cache", None)
|
|
27
27
|
if cache and hasattr(cache, "get_error_patterns"):
|
|
28
|
-
|
|
28
|
+
patterns: list[t.Any] = cache.get_error_patterns()
|
|
29
|
+
return patterns
|
|
29
30
|
|
|
30
31
|
return []
|
|
31
32
|
|
|
@@ -70,7 +71,7 @@ def _build_error_analysis(patterns: list[t.Any], context: t.Any) -> dict[str, t.
|
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
def _categorize_error_patterns(patterns: list[t.Any]) -> dict[str, list[t.Any]]:
|
|
73
|
-
categories = {
|
|
74
|
+
categories: dict[str, list[t.Any]] = {
|
|
74
75
|
"syntax_errors": [],
|
|
75
76
|
"import_errors": [],
|
|
76
77
|
"type_errors": [],
|
|
@@ -15,7 +15,7 @@ def register_execution_tools(mcp_app: t.Any) -> None:
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def _register_execute_crackerjack_tool(mcp_app: t.Any) -> None:
|
|
18
|
-
@mcp_app.tool()
|
|
18
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
19
19
|
async def execute_crackerjack(args: str, kwargs: str) -> str:
|
|
20
20
|
context = get_context()
|
|
21
21
|
|
|
@@ -29,13 +29,11 @@ def _register_execute_crackerjack_tool(mcp_app: t.Any) -> None:
|
|
|
29
29
|
|
|
30
30
|
extra_kwargs = kwargs_result["kwargs"]
|
|
31
31
|
|
|
32
|
-
# Add extended timeout for long-running operations
|
|
33
32
|
if "execution_timeout" not in extra_kwargs:
|
|
34
|
-
# Default to 15 minutes, extend to 20 minutes for test operations
|
|
35
33
|
if extra_kwargs.get("test", False) or extra_kwargs.get("testing", False):
|
|
36
|
-
extra_kwargs["execution_timeout"] = 1200
|
|
34
|
+
extra_kwargs["execution_timeout"] = 1200
|
|
37
35
|
else:
|
|
38
|
-
extra_kwargs["execution_timeout"] = 900
|
|
36
|
+
extra_kwargs["execution_timeout"] = 900
|
|
39
37
|
|
|
40
38
|
try:
|
|
41
39
|
result = await execute_crackerjack_workflow(args, extra_kwargs)
|
|
@@ -53,7 +51,7 @@ def _register_execute_crackerjack_tool(mcp_app: t.Any) -> None:
|
|
|
53
51
|
|
|
54
52
|
|
|
55
53
|
def _register_smart_error_analysis_tool(mcp_app: t.Any) -> None:
|
|
56
|
-
@mcp_app.tool()
|
|
54
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
57
55
|
async def smart_error_analysis(use_cache: bool = True) -> str:
|
|
58
56
|
context = get_context()
|
|
59
57
|
|
|
@@ -71,7 +69,7 @@ def _register_smart_error_analysis_tool(mcp_app: t.Any) -> None:
|
|
|
71
69
|
|
|
72
70
|
|
|
73
71
|
def _register_init_crackerjack_tool(mcp_app: t.Any) -> None:
|
|
74
|
-
@mcp_app.tool()
|
|
72
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
75
73
|
def init_crackerjack(args: str = "", kwargs: str = "{}") -> str:
|
|
76
74
|
try:
|
|
77
75
|
target_path, force, error = _parse_init_arguments(args, kwargs)
|
|
@@ -86,7 +84,7 @@ def _register_init_crackerjack_tool(mcp_app: t.Any) -> None:
|
|
|
86
84
|
|
|
87
85
|
|
|
88
86
|
def _register_agent_suggestions_tool(mcp_app: t.Any) -> None:
|
|
89
|
-
@mcp_app.tool()
|
|
87
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
90
88
|
def suggest_agents(
|
|
91
89
|
task_description: str = "",
|
|
92
90
|
project_type: str = "python",
|
|
@@ -139,7 +137,7 @@ def _parse_kwargs(kwargs: str) -> dict[str, t.Any]:
|
|
|
139
137
|
def _parse_init_arguments(args: str, kwargs: str) -> tuple[t.Any, bool, str | None]:
|
|
140
138
|
try:
|
|
141
139
|
target_path = args.strip() or "."
|
|
142
|
-
kwargs_dict = json.loads(kwargs) if kwargs.strip() else {}
|
|
140
|
+
kwargs_dict: dict[str, t.Any] = json.loads(kwargs) if kwargs.strip() else {}
|
|
143
141
|
force = kwargs_dict.get("force") or False
|
|
144
142
|
|
|
145
143
|
from pathlib import Path
|
|
@@ -165,7 +163,7 @@ def _execute_initialization(target_path: t.Any, force: bool) -> dict[str, t.Any]
|
|
|
165
163
|
from crackerjack.services.git import GitService
|
|
166
164
|
|
|
167
165
|
filesystem = FileSystemService()
|
|
168
|
-
git_service = GitService()
|
|
166
|
+
git_service = GitService(console)
|
|
169
167
|
return InitializationService(
|
|
170
168
|
console, filesystem, git_service, target_path
|
|
171
169
|
).initialize_project_full(force=force)
|
|
@@ -85,7 +85,7 @@ async def _validate_context_and_rate_limit(context: t.Any) -> str | None:
|
|
|
85
85
|
return None
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def _handle_task_exception(job_id: str, task: asyncio.Task) -> None:
|
|
88
|
+
def _handle_task_exception(job_id: str, task: asyncio.Task[t.Any]) -> None:
|
|
89
89
|
import tempfile
|
|
90
90
|
from pathlib import Path
|
|
91
91
|
|
|
@@ -405,10 +405,9 @@ def _create_workflow_options(kwargs: dict[str, t.Any]) -> t.Any:
|
|
|
405
405
|
|
|
406
406
|
options = WorkflowOptions()
|
|
407
407
|
options.testing.test = kwargs.get("test", True)
|
|
408
|
-
options.ai_agent = kwargs.get("ai_agent", True)
|
|
409
|
-
options.skip_hooks = kwargs.get("skip_hooks", False)
|
|
408
|
+
options.ai.ai_agent = kwargs.get("ai_agent", True)
|
|
409
|
+
options.hooks.skip_hooks = kwargs.get("skip_hooks", False)
|
|
410
410
|
|
|
411
|
-
options.proactive_mode = kwargs.get("proactive_mode", True)
|
|
412
411
|
return options
|
|
413
412
|
|
|
414
413
|
|
|
@@ -418,8 +417,10 @@ async def _execute_single_iteration(
|
|
|
418
417
|
options: t.Any,
|
|
419
418
|
) -> bool:
|
|
420
419
|
if use_advanced_orchestrator:
|
|
421
|
-
|
|
422
|
-
|
|
420
|
+
result = await orchestrator.execute_orchestrated_workflow(options)
|
|
421
|
+
return bool(result)
|
|
422
|
+
result = await orchestrator.run_complete_workflow(options)
|
|
423
|
+
return bool(result)
|
|
423
424
|
|
|
424
425
|
|
|
425
426
|
def _create_success_result(
|
|
@@ -715,6 +716,8 @@ def _register_init_crackerjack_tool(mcp_app: t.Any) -> None:
|
|
|
715
716
|
|
|
716
717
|
try:
|
|
717
718
|
results = _execute_initialization(context, target_path, force)
|
|
719
|
+
if isinstance(results, bool):
|
|
720
|
+
return json.dumps({"success": results})
|
|
718
721
|
return _create_init_success_response(results, target_path, force)
|
|
719
722
|
except Exception as e:
|
|
720
723
|
return _create_init_exception_response(e, target_path)
|
|
@@ -730,18 +733,20 @@ def _parse_init_arguments(args: str, kwargs: str) -> tuple[t.Any, bool, str | No
|
|
|
730
733
|
target_path = args.strip() or None
|
|
731
734
|
|
|
732
735
|
try:
|
|
733
|
-
extra_kwargs = json.loads(kwargs) if kwargs.strip() else {}
|
|
736
|
+
extra_kwargs: dict[str, t.Any] = json.loads(kwargs) if kwargs.strip() else {}
|
|
734
737
|
except json.JSONDecodeError as e:
|
|
735
738
|
return None, False, _create_init_error_response(f"Invalid JSON in kwargs: {e}")
|
|
736
739
|
|
|
737
740
|
force = extra_kwargs.get("force", False)
|
|
738
741
|
|
|
739
742
|
if target_path:
|
|
740
|
-
|
|
743
|
+
target_path_obj = Path(target_path).resolve()
|
|
744
|
+
target_path = str(target_path_obj)
|
|
741
745
|
else:
|
|
742
|
-
|
|
746
|
+
target_path_obj = Path.cwd()
|
|
747
|
+
target_path = str(target_path_obj)
|
|
743
748
|
|
|
744
|
-
if not target_path.exists():
|
|
749
|
+
if not Path(target_path).exists():
|
|
745
750
|
return (
|
|
746
751
|
None,
|
|
747
752
|
False,
|
|
@@ -751,9 +756,7 @@ def _parse_init_arguments(args: str, kwargs: str) -> tuple[t.Any, bool, str | No
|
|
|
751
756
|
return target_path, force, None
|
|
752
757
|
|
|
753
758
|
|
|
754
|
-
def _execute_initialization(
|
|
755
|
-
context: t.Any, target_path: t.Any, force: bool
|
|
756
|
-
) -> dict[str, t.Any]:
|
|
759
|
+
def _execute_initialization(context: t.Any, target_path: t.Any, force: bool) -> bool:
|
|
757
760
|
from crackerjack.services.filesystem import FileSystemService
|
|
758
761
|
from crackerjack.services.git import GitService
|
|
759
762
|
from crackerjack.services.initialization import InitializationService
|
|
@@ -792,15 +795,24 @@ def _register_agent_suggestions_tool(mcp_app: t.Any) -> None:
|
|
|
792
795
|
project_type: str = "python",
|
|
793
796
|
current_context: str = "",
|
|
794
797
|
) -> str:
|
|
795
|
-
suggestions = {
|
|
796
|
-
"primary_agents": [],
|
|
797
|
-
"task_specific_agents": [],
|
|
798
|
-
"usage_patterns": [],
|
|
798
|
+
suggestions: dict[str, list[dict[str, str]] | list[str] | str] = {
|
|
799
|
+
"primary_agents": t.cast(list[dict[str, str]], []),
|
|
800
|
+
"task_specific_agents": t.cast(list[dict[str, str]], []),
|
|
801
|
+
"usage_patterns": t.cast(list[str], []),
|
|
799
802
|
"rationale": "",
|
|
800
803
|
}
|
|
801
804
|
|
|
805
|
+
# Type cast the lists to ensure proper typing
|
|
806
|
+
primary_agents: list[dict[str, str]] = t.cast(
|
|
807
|
+
list[dict[str, str]], suggestions["primary_agents"]
|
|
808
|
+
)
|
|
809
|
+
task_specific_agents: list[dict[str, str]] = t.cast(
|
|
810
|
+
list[dict[str, str]], suggestions["task_specific_agents"]
|
|
811
|
+
)
|
|
812
|
+
t.cast(list[str], suggestions["usage_patterns"])
|
|
813
|
+
|
|
802
814
|
if project_type.lower() == "python" or "python" in task_description.lower():
|
|
803
|
-
|
|
815
|
+
primary_agents.append(
|
|
804
816
|
{
|
|
805
817
|
"name": "crackerjack-architect",
|
|
806
818
|
"emoji": "๐๏ธ",
|
|
@@ -810,11 +822,11 @@ def _register_agent_suggestions_tool(mcp_app: t.Any) -> None:
|
|
|
810
822
|
}
|
|
811
823
|
)
|
|
812
824
|
|
|
813
|
-
|
|
825
|
+
primary_agents.append(
|
|
814
826
|
{
|
|
815
827
|
"name": "python-pro",
|
|
816
828
|
"emoji": "๐",
|
|
817
|
-
"description": "Modern Python development with type hints, async
|
|
829
|
+
"description": "Modern Python development with type hints, async/await patterns, and clean architecture",
|
|
818
830
|
"usage": "Use for implementing Python code with best practices",
|
|
819
831
|
"priority": "HIGH",
|
|
820
832
|
}
|
|
@@ -827,9 +839,9 @@ def _register_agent_suggestions_tool(mcp_app: t.Any) -> None:
|
|
|
827
839
|
word in task_lower + context_lower
|
|
828
840
|
for word in ("test", "testing", "coverage", "pytest")
|
|
829
841
|
):
|
|
830
|
-
|
|
842
|
+
task_specific_agents.append(
|
|
831
843
|
{
|
|
832
|
-
"name": "crackerjack
|
|
844
|
+
"name": "crackerjack-test-specialist",
|
|
833
845
|
"emoji": "๐งช",
|
|
834
846
|
"description": "Advanced testing specialist for complex scenarios and coverage optimization",
|
|
835
847
|
"usage": "Use for test creation, debugging test failures, and coverage improvements",
|
|
@@ -837,9 +849,9 @@ def _register_agent_suggestions_tool(mcp_app: t.Any) -> None:
|
|
|
837
849
|
}
|
|
838
850
|
)
|
|
839
851
|
|
|
840
|
-
|
|
852
|
+
task_specific_agents.append(
|
|
841
853
|
{
|
|
842
|
-
"name": "pytest
|
|
854
|
+
"name": "pytest-hypothesis-specialist",
|
|
843
855
|
"emoji": "๐งช",
|
|
844
856
|
"description": "Advanced testing patterns and property-based testing",
|
|
845
857
|
"usage": "Use for comprehensive test development and optimization",
|
|
@@ -851,12 +863,12 @@ def _register_agent_suggestions_tool(mcp_app: t.Any) -> None:
|
|
|
851
863
|
word in task_lower + context_lower
|
|
852
864
|
for word in ("security", "vulnerability", "auth", "permission")
|
|
853
865
|
):
|
|
854
|
-
|
|
866
|
+
task_specific_agents.append(
|
|
855
867
|
{
|
|
856
868
|
"name": "security-auditor",
|
|
857
869
|
"emoji": "๐",
|
|
858
|
-
"description": "Security
|
|
859
|
-
"usage": "Use for
|
|
870
|
+
"description": "Security auditing and vulnerability detection specialist",
|
|
871
|
+
"usage": "Use for identifying and mitigating security vulnerabilities",
|
|
860
872
|
"priority": "HIGH",
|
|
861
873
|
}
|
|
862
874
|
)
|
|
@@ -865,12 +877,12 @@ def _register_agent_suggestions_tool(mcp_app: t.Any) -> None:
|
|
|
865
877
|
word in task_lower + context_lower
|
|
866
878
|
for word in ("architecture", "design", "api", "backend")
|
|
867
879
|
):
|
|
868
|
-
|
|
880
|
+
task_specific_agents.append(
|
|
869
881
|
{
|
|
870
882
|
"name": "backend-architect",
|
|
871
883
|
"emoji": "๐๏ธ",
|
|
872
|
-
"description": "
|
|
873
|
-
"usage": "Use for
|
|
884
|
+
"description": "Backend architecture and system design specialist",
|
|
885
|
+
"usage": "Use for complex backend design decisions and system architecture",
|
|
874
886
|
"priority": "MEDIUM",
|
|
875
887
|
}
|
|
876
888
|
)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
|
|
1
3
|
from .intelligence_tools import (
|
|
2
4
|
analyze_agent_performance,
|
|
3
5
|
execute_smart_agent_task,
|
|
@@ -6,7 +8,7 @@ from .intelligence_tools import (
|
|
|
6
8
|
)
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
def register_intelligence_tools(mcp_app) -> None:
|
|
11
|
+
def register_intelligence_tools(mcp_app: t.Any) -> None:
|
|
10
12
|
@mcp_app.tool()
|
|
11
13
|
async def execute_smart_task(
|
|
12
14
|
task_description: str,
|
|
@@ -14,7 +16,7 @@ def register_intelligence_tools(mcp_app) -> None:
|
|
|
14
16
|
strategy: str = "single_best",
|
|
15
17
|
max_agents: int = 3,
|
|
16
18
|
use_learning: bool = True,
|
|
17
|
-
):
|
|
19
|
+
) -> t.Any:
|
|
18
20
|
return await execute_smart_agent_task(
|
|
19
21
|
task_description,
|
|
20
22
|
context_type,
|
|
@@ -28,7 +30,7 @@ def register_intelligence_tools(mcp_app) -> None:
|
|
|
28
30
|
task_description: str,
|
|
29
31
|
context_type: str = "general",
|
|
30
32
|
include_analysis: bool = True,
|
|
31
|
-
):
|
|
33
|
+
) -> t.Any:
|
|
32
34
|
return await get_smart_agent_recommendation(
|
|
33
35
|
task_description,
|
|
34
36
|
context_type,
|
|
@@ -36,9 +38,9 @@ def register_intelligence_tools(mcp_app) -> None:
|
|
|
36
38
|
)
|
|
37
39
|
|
|
38
40
|
@mcp_app.tool()
|
|
39
|
-
async def intelligence_system_status():
|
|
41
|
+
async def intelligence_system_status() -> t.Any:
|
|
40
42
|
return await get_intelligence_system_status()
|
|
41
43
|
|
|
42
44
|
@mcp_app.tool()
|
|
43
|
-
async def agent_performance_analysis():
|
|
45
|
+
async def agent_performance_analysis() -> t.Any:
|
|
44
46
|
return await analyze_agent_performance()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import json
|
|
2
3
|
import logging
|
|
3
4
|
import typing as t
|
|
4
5
|
|
|
@@ -173,10 +174,12 @@ async def get_smart_agent_recommendation(
|
|
|
173
174
|
if include_analysis:
|
|
174
175
|
try:
|
|
175
176
|
analysis = await system.analyze_task_complexity(task_description)
|
|
176
|
-
response["complexity_analysis"] = analysis
|
|
177
|
+
response["complexity_analysis"] = json.dumps(analysis, indent=2)
|
|
177
178
|
except Exception as e:
|
|
178
179
|
logger.warning(f"Failed to analyze task complexity: {e}")
|
|
179
|
-
response["complexity_analysis"] =
|
|
180
|
+
response["complexity_analysis"] = json.dumps(
|
|
181
|
+
{"error": str(e)}, indent=2
|
|
182
|
+
)
|
|
180
183
|
|
|
181
184
|
logger.debug(f"Generated recommendation for task: {task_description[:50]}...")
|
|
182
185
|
return response
|