crackerjack 0.32.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 +64 -6
- 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 +257 -218
- 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 +558 -240
- 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 +66 -13
- crackerjack/managers/test_command_builder.py +5 -17
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +109 -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 +161 -32
- 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 +174 -33
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +15 -12
- 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 +3 -0
- crackerjack/mixins/error_handling.py +145 -0
- crackerjack/models/config.py +21 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +176 -107
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/models/task.py +3 -0
- 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 +90 -105
- 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 +18 -11
- crackerjack/services/config_merge.py +30 -85
- 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 +41 -17
- 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 +41 -45
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +30 -33
- 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 +409 -0
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +416 -0
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +250 -576
- crackerjack/services/performance_cache.py +382 -0
- crackerjack/services/performance_monitor.py +565 -0
- 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 +61 -30
- crackerjack/services/security_logger.py +18 -22
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/terminal_utils.py +0 -0
- 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.32.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +197 -26
- 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.32.0.dist-info/RECORD +0 -180
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
crackerjack/mcp/state.py
CHANGED
|
@@ -92,7 +92,9 @@ class SessionState:
|
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
class StateManager:
|
|
95
|
-
def __init__(
|
|
95
|
+
def __init__(
|
|
96
|
+
self, state_dir: Path | None = None, batched_saver: t.Any | None = None
|
|
97
|
+
) -> None:
|
|
96
98
|
self._lock = asyncio.Lock()
|
|
97
99
|
self.state_dir = state_dir or Path.home() / ".cache" / "crackerjack-mcp"
|
|
98
100
|
self.state_dir.mkdir(exist_ok=True)
|
|
@@ -258,7 +260,7 @@ class StateManager:
|
|
|
258
260
|
priority_counts = {}
|
|
259
261
|
for priority in Priority:
|
|
260
262
|
priority_counts[priority.value] = len(self.get_issues_by_priority(priority))
|
|
261
|
-
type_counts = {}
|
|
263
|
+
type_counts: dict[str, int] = {}
|
|
262
264
|
for issue in issues:
|
|
263
265
|
type_counts[issue.type] = type_counts.get(issue.type, 0) + 1
|
|
264
266
|
stage_status = {}
|
|
@@ -351,14 +353,9 @@ class StateManager:
|
|
|
351
353
|
return checkpoints
|
|
352
354
|
|
|
353
355
|
def start_session(self) -> None:
|
|
354
|
-
"""Start or initialize a session."""
|
|
355
|
-
# Session is already initialized in __init__, this is a no-op
|
|
356
|
-
# but provided for API compatibility
|
|
357
356
|
self._save_state()
|
|
358
357
|
|
|
359
358
|
def complete_session(self) -> None:
|
|
360
|
-
"""Complete the current session."""
|
|
361
|
-
# Mark session as complete in metadata
|
|
362
359
|
if not self.session_state.metadata:
|
|
363
360
|
self.session_state.metadata = {}
|
|
364
361
|
self.session_state.metadata["status"] = "completed"
|
crackerjack/mcp/task_manager.py
CHANGED
|
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
@dataclass
|
|
15
15
|
class TaskInfo:
|
|
16
16
|
task_id: str
|
|
17
|
-
task: asyncio.Task
|
|
17
|
+
task: asyncio.Task[t.Any]
|
|
18
18
|
created_at: float
|
|
19
19
|
description: str = ""
|
|
20
20
|
timeout_seconds: float | None = None
|
|
@@ -25,7 +25,7 @@ class AsyncTaskManager:
|
|
|
25
25
|
self.max_concurrent_tasks = max_concurrent_tasks
|
|
26
26
|
self._tasks: dict[str, TaskInfo] = {}
|
|
27
27
|
self._task_semaphore = asyncio.Semaphore(max_concurrent_tasks)
|
|
28
|
-
self._cleanup_task: asyncio.Task | None = None
|
|
28
|
+
self._cleanup_task: asyncio.Task[t.Any] | None = None
|
|
29
29
|
self._running = False
|
|
30
30
|
self._lock = asyncio.Lock()
|
|
31
31
|
|
|
@@ -61,11 +61,11 @@ class AsyncTaskManager:
|
|
|
61
61
|
|
|
62
62
|
async def create_task(
|
|
63
63
|
self,
|
|
64
|
-
coro: t.Coroutine,
|
|
64
|
+
coro: t.Coroutine[t.Any, t.Any, t.Any],
|
|
65
65
|
task_id: str,
|
|
66
66
|
description: str = "",
|
|
67
67
|
timeout_seconds: float | None = None,
|
|
68
|
-
) -> asyncio.Task:
|
|
68
|
+
) -> asyncio.Task[t.Any]:
|
|
69
69
|
async with self._lock:
|
|
70
70
|
if task_id in self._tasks:
|
|
71
71
|
msg = f"Task {task_id} already exists"
|
|
@@ -100,7 +100,9 @@ class AsyncTaskManager:
|
|
|
100
100
|
console.print(f"[blue]🚀 Task {task_id} created: {description}[ / blue]")
|
|
101
101
|
return task
|
|
102
102
|
|
|
103
|
-
async def _wrap_task(
|
|
103
|
+
async def _wrap_task(
|
|
104
|
+
self, coro: t.Coroutine[t.Any, t.Any, t.Any], task_id: str
|
|
105
|
+
) -> t.Any:
|
|
104
106
|
try:
|
|
105
107
|
async with self._task_semaphore:
|
|
106
108
|
result = await coro
|
|
@@ -185,11 +187,11 @@ class AsyncTaskManager:
|
|
|
185
187
|
@asynccontextmanager
|
|
186
188
|
async def managed_task(
|
|
187
189
|
self,
|
|
188
|
-
coro: t.Coroutine,
|
|
190
|
+
coro: t.Coroutine[t.Any, t.Any, t.Any],
|
|
189
191
|
task_id: str,
|
|
190
192
|
description: str = "",
|
|
191
193
|
timeout_seconds: float | None = None,
|
|
192
|
-
):
|
|
194
|
+
) -> t.AsyncGenerator[asyncio.Task[t.Any]]:
|
|
193
195
|
task = await self.create_task(coro, task_id, description, timeout_seconds)
|
|
194
196
|
try:
|
|
195
197
|
yield task
|
|
@@ -201,7 +203,7 @@ class AsyncTaskManager:
|
|
|
201
203
|
|
|
202
204
|
async def _cancel_all_tasks(self) -> None:
|
|
203
205
|
async with self._lock:
|
|
204
|
-
tasks_to_cancel = list(self._tasks.values())
|
|
206
|
+
tasks_to_cancel = list[t.Any](self._tasks.values())
|
|
205
207
|
|
|
206
208
|
if not tasks_to_cancel:
|
|
207
209
|
return
|
|
@@ -240,7 +242,7 @@ class AsyncTaskManager:
|
|
|
240
242
|
async def _cleanup_completed_tasks(self) -> None:
|
|
241
243
|
async with self._lock:
|
|
242
244
|
completed_tasks = []
|
|
243
|
-
for task_id, task_info in list(self._tasks.items()):
|
|
245
|
+
for task_id, task_info in list[t.Any](self._tasks.items()):
|
|
244
246
|
if task_info.task.done():
|
|
245
247
|
completed_tasks.append(task_id)
|
|
246
248
|
del self._tasks[task_id]
|
|
@@ -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
|
|
|
@@ -186,9 +326,10 @@ def _execute_init_stage(orchestrator) -> bool:
|
|
|
186
326
|
|
|
187
327
|
init_service = InitializationService(console, filesystem, git_service, pkg_path)
|
|
188
328
|
|
|
189
|
-
results = init_service.
|
|
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
|
|
@@ -161,9 +159,14 @@ def _execute_initialization(target_path: t.Any, force: bool) -> dict[str, t.Any]
|
|
|
161
159
|
|
|
162
160
|
console = Console()
|
|
163
161
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
from crackerjack.services.filesystem import FileSystemService
|
|
163
|
+
from crackerjack.services.git import GitService
|
|
164
|
+
|
|
165
|
+
filesystem = FileSystemService()
|
|
166
|
+
git_service = GitService(console)
|
|
167
|
+
return InitializationService(
|
|
168
|
+
console, filesystem, git_service, target_path
|
|
169
|
+
).initialize_project_full(force=force)
|
|
167
170
|
|
|
168
171
|
|
|
169
172
|
def _create_init_error_response(message: str) -> str:
|