crackerjack 0.31.10__py3-none-any.whl → 0.31.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- crackerjack/CLAUDE.md +288 -705
- crackerjack/__main__.py +22 -8
- crackerjack/agents/__init__.py +0 -3
- crackerjack/agents/architect_agent.py +0 -43
- crackerjack/agents/base.py +1 -9
- crackerjack/agents/coordinator.py +2 -148
- crackerjack/agents/documentation_agent.py +109 -81
- crackerjack/agents/dry_agent.py +122 -97
- crackerjack/agents/formatting_agent.py +3 -16
- crackerjack/agents/import_optimization_agent.py +1174 -130
- crackerjack/agents/performance_agent.py +956 -188
- crackerjack/agents/performance_helpers.py +229 -0
- crackerjack/agents/proactive_agent.py +1 -48
- crackerjack/agents/refactoring_agent.py +516 -246
- crackerjack/agents/refactoring_helpers.py +282 -0
- crackerjack/agents/security_agent.py +393 -90
- crackerjack/agents/test_creation_agent.py +1776 -120
- crackerjack/agents/test_specialist_agent.py +59 -15
- crackerjack/agents/tracker.py +0 -102
- crackerjack/api.py +145 -37
- crackerjack/cli/handlers.py +48 -30
- crackerjack/cli/interactive.py +11 -11
- crackerjack/cli/options.py +66 -4
- crackerjack/code_cleaner.py +808 -148
- crackerjack/config/global_lock_config.py +110 -0
- crackerjack/config/hooks.py +43 -64
- crackerjack/core/async_workflow_orchestrator.py +247 -97
- crackerjack/core/autofix_coordinator.py +192 -109
- crackerjack/core/enhanced_container.py +46 -63
- crackerjack/core/file_lifecycle.py +549 -0
- crackerjack/core/performance.py +9 -8
- crackerjack/core/performance_monitor.py +395 -0
- crackerjack/core/phase_coordinator.py +281 -94
- crackerjack/core/proactive_workflow.py +9 -58
- crackerjack/core/resource_manager.py +501 -0
- crackerjack/core/service_watchdog.py +490 -0
- crackerjack/core/session_coordinator.py +4 -8
- crackerjack/core/timeout_manager.py +504 -0
- crackerjack/core/websocket_lifecycle.py +475 -0
- crackerjack/core/workflow_orchestrator.py +343 -209
- crackerjack/dynamic_config.py +50 -9
- crackerjack/errors.py +3 -4
- crackerjack/executors/async_hook_executor.py +63 -13
- crackerjack/executors/cached_hook_executor.py +14 -14
- crackerjack/executors/hook_executor.py +100 -37
- crackerjack/executors/hook_lock_manager.py +856 -0
- crackerjack/executors/individual_hook_executor.py +120 -86
- crackerjack/intelligence/__init__.py +0 -7
- crackerjack/intelligence/adaptive_learning.py +13 -86
- crackerjack/intelligence/agent_orchestrator.py +15 -78
- crackerjack/intelligence/agent_registry.py +12 -59
- crackerjack/intelligence/agent_selector.py +31 -92
- crackerjack/intelligence/integration.py +1 -41
- crackerjack/interactive.py +9 -9
- crackerjack/managers/async_hook_manager.py +25 -8
- crackerjack/managers/hook_manager.py +9 -9
- crackerjack/managers/publish_manager.py +57 -59
- crackerjack/managers/test_command_builder.py +6 -36
- crackerjack/managers/test_executor.py +9 -61
- crackerjack/managers/test_manager.py +17 -63
- crackerjack/managers/test_manager_backup.py +77 -127
- crackerjack/managers/test_progress.py +4 -23
- crackerjack/mcp/cache.py +5 -12
- crackerjack/mcp/client_runner.py +10 -10
- crackerjack/mcp/context.py +64 -6
- crackerjack/mcp/dashboard.py +14 -11
- crackerjack/mcp/enhanced_progress_monitor.py +55 -55
- crackerjack/mcp/file_monitor.py +72 -42
- crackerjack/mcp/progress_components.py +103 -84
- crackerjack/mcp/progress_monitor.py +122 -49
- crackerjack/mcp/rate_limiter.py +12 -12
- crackerjack/mcp/server_core.py +16 -22
- crackerjack/mcp/service_watchdog.py +26 -26
- crackerjack/mcp/state.py +15 -0
- crackerjack/mcp/tools/core_tools.py +95 -39
- crackerjack/mcp/tools/error_analyzer.py +6 -32
- crackerjack/mcp/tools/execution_tools.py +1 -56
- crackerjack/mcp/tools/execution_tools_backup.py +35 -131
- crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
- crackerjack/mcp/tools/intelligence_tools.py +2 -55
- crackerjack/mcp/tools/monitoring_tools.py +308 -145
- crackerjack/mcp/tools/proactive_tools.py +12 -42
- crackerjack/mcp/tools/progress_tools.py +23 -15
- crackerjack/mcp/tools/utility_tools.py +3 -40
- crackerjack/mcp/tools/workflow_executor.py +40 -60
- crackerjack/mcp/websocket/app.py +0 -3
- crackerjack/mcp/websocket/endpoints.py +206 -268
- crackerjack/mcp/websocket/jobs.py +213 -66
- crackerjack/mcp/websocket/server.py +84 -6
- crackerjack/mcp/websocket/websocket_handler.py +137 -29
- crackerjack/models/config_adapter.py +3 -16
- crackerjack/models/protocols.py +162 -3
- crackerjack/models/resource_protocols.py +454 -0
- crackerjack/models/task.py +3 -3
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +25 -71
- crackerjack/monitoring/regression_prevention.py +28 -87
- crackerjack/orchestration/advanced_orchestrator.py +44 -78
- crackerjack/orchestration/coverage_improvement.py +10 -60
- crackerjack/orchestration/execution_strategies.py +16 -16
- crackerjack/orchestration/test_progress_streamer.py +61 -53
- crackerjack/plugins/base.py +1 -1
- crackerjack/plugins/managers.py +22 -20
- crackerjack/py313.py +65 -21
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +627 -0
- crackerjack/services/cache.py +7 -9
- crackerjack/services/config.py +35 -52
- crackerjack/services/config_integrity.py +5 -16
- crackerjack/services/config_merge.py +542 -0
- crackerjack/services/contextual_ai_assistant.py +17 -19
- crackerjack/services/coverage_ratchet.py +44 -73
- crackerjack/services/debug.py +25 -39
- crackerjack/services/dependency_monitor.py +52 -50
- crackerjack/services/enhanced_filesystem.py +14 -11
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/filesystem.py +1 -12
- crackerjack/services/git.py +71 -47
- crackerjack/services/health_metrics.py +31 -27
- crackerjack/services/initialization.py +276 -428
- crackerjack/services/input_validator.py +760 -0
- crackerjack/services/log_manager.py +16 -16
- crackerjack/services/logging.py +7 -6
- crackerjack/services/metrics.py +43 -43
- crackerjack/services/pattern_cache.py +2 -31
- crackerjack/services/pattern_detector.py +26 -63
- crackerjack/services/performance_benchmarks.py +20 -45
- crackerjack/services/regex_patterns.py +2887 -0
- crackerjack/services/regex_utils.py +537 -0
- crackerjack/services/secure_path_utils.py +683 -0
- crackerjack/services/secure_status_formatter.py +534 -0
- crackerjack/services/secure_subprocess.py +605 -0
- crackerjack/services/security.py +47 -10
- crackerjack/services/security_logger.py +492 -0
- crackerjack/services/server_manager.py +109 -50
- crackerjack/services/smart_scheduling.py +8 -25
- crackerjack/services/status_authentication.py +603 -0
- crackerjack/services/status_security_manager.py +442 -0
- crackerjack/services/thread_safe_status_collector.py +546 -0
- crackerjack/services/tool_version_service.py +1 -23
- crackerjack/services/unified_config.py +36 -58
- crackerjack/services/validation_rate_limiter.py +269 -0
- crackerjack/services/version_checker.py +9 -40
- crackerjack/services/websocket_resource_limiter.py +572 -0
- crackerjack/slash_commands/__init__.py +52 -2
- crackerjack/tools/__init__.py +0 -0
- crackerjack/tools/validate_input_validator_patterns.py +262 -0
- crackerjack/tools/validate_regex_patterns.py +198 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/METADATA +197 -12
- crackerjack-0.31.13.dist-info/RECORD +178 -0
- crackerjack/cli/facade.py +0 -104
- crackerjack-0.31.10.dist-info/RECORD +0 -149
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/WHEEL +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,10 +4,32 @@ import typing as t
|
|
|
4
4
|
from contextlib import suppress
|
|
5
5
|
|
|
6
6
|
from crackerjack.mcp.context import get_context
|
|
7
|
+
from crackerjack.services.bounded_status_operations import (
|
|
8
|
+
execute_bounded_status_operation,
|
|
9
|
+
)
|
|
10
|
+
from crackerjack.services.secure_status_formatter import (
|
|
11
|
+
StatusVerbosity,
|
|
12
|
+
format_secure_status,
|
|
13
|
+
get_secure_status_formatter,
|
|
14
|
+
)
|
|
15
|
+
from crackerjack.services.status_authentication import (
|
|
16
|
+
authenticate_status_request,
|
|
17
|
+
get_status_authenticator,
|
|
18
|
+
)
|
|
19
|
+
from crackerjack.services.status_security_manager import (
|
|
20
|
+
get_status_security_manager,
|
|
21
|
+
secure_status_operation,
|
|
22
|
+
validate_status_request,
|
|
23
|
+
)
|
|
24
|
+
from crackerjack.services.thread_safe_status_collector import (
|
|
25
|
+
get_thread_safe_status_collector,
|
|
26
|
+
)
|
|
27
|
+
from crackerjack.services.websocket_resource_limiter import (
|
|
28
|
+
get_websocket_resource_limiter,
|
|
29
|
+
)
|
|
7
30
|
|
|
8
31
|
|
|
9
32
|
def _suggest_agent_for_context(state_manager) -> dict[str, t.Any]:
|
|
10
|
-
"""Suggest appropriate agents based on current development context."""
|
|
11
33
|
suggestions = {
|
|
12
34
|
"recommended_agent": None,
|
|
13
35
|
"reason": "",
|
|
@@ -15,24 +37,22 @@ def _suggest_agent_for_context(state_manager) -> dict[str, t.Any]:
|
|
|
15
37
|
"priority": "MEDIUM",
|
|
16
38
|
}
|
|
17
39
|
|
|
18
|
-
# Check for errors or failures that need specific agents
|
|
19
40
|
with suppress(Exception):
|
|
20
41
|
recent_errors = getattr(state_manager, "recent_errors", [])
|
|
21
42
|
stage_statuses = _get_stage_status_dict(state_manager)
|
|
22
43
|
|
|
23
|
-
# Test failures suggest test specialist
|
|
24
44
|
if stage_statuses.get("tests") == "failed" or any(
|
|
25
45
|
"test" in str(error).lower() for error in recent_errors
|
|
26
46
|
):
|
|
27
47
|
suggestions.update(
|
|
28
48
|
{
|
|
29
|
-
"recommended_agent": "crackerjack-test-specialist",
|
|
30
|
-
"reason": "Test failures detected
|
|
31
|
-
"usage": 'Task tool with subagent_type="crackerjack-test-specialist"',
|
|
49
|
+
"recommended_agent": "crackerjack - test-specialist",
|
|
50
|
+
"reason": "Test failures detected-specialist needed for debugging and fixes",
|
|
51
|
+
"usage": 'Task tool with subagent_type ="crackerjack - test-specialist"',
|
|
32
52
|
"priority": "HIGH",
|
|
33
53
|
}
|
|
34
54
|
)
|
|
35
|
-
|
|
55
|
+
|
|
36
56
|
elif any(
|
|
37
57
|
"security" in str(error).lower() or "bandit" in str(error).lower()
|
|
38
58
|
for error in recent_errors
|
|
@@ -40,28 +60,28 @@ def _suggest_agent_for_context(state_manager) -> dict[str, t.Any]:
|
|
|
40
60
|
suggestions.update(
|
|
41
61
|
{
|
|
42
62
|
"recommended_agent": "security-auditor",
|
|
43
|
-
"reason": "Security issues detected
|
|
44
|
-
"usage": 'Task tool with subagent_type="security-auditor"',
|
|
63
|
+
"reason": "Security issues detected-immediate audit required",
|
|
64
|
+
"usage": 'Task tool with subagent_type ="security-auditor"',
|
|
45
65
|
"priority": "HIGH",
|
|
46
66
|
}
|
|
47
67
|
)
|
|
48
|
-
|
|
68
|
+
|
|
49
69
|
elif any("complex" in str(error).lower() for error in recent_errors):
|
|
50
70
|
suggestions.update(
|
|
51
71
|
{
|
|
52
72
|
"recommended_agent": "crackerjack-architect",
|
|
53
|
-
"reason": "Complexity issues detected
|
|
54
|
-
"usage": 'Task tool with subagent_type="crackerjack-architect"',
|
|
73
|
+
"reason": "Complexity issues detected-architectural review needed",
|
|
74
|
+
"usage": 'Task tool with subagent_type ="crackerjack-architect"',
|
|
55
75
|
"priority": "HIGH",
|
|
56
76
|
}
|
|
57
77
|
)
|
|
58
|
-
|
|
78
|
+
|
|
59
79
|
else:
|
|
60
80
|
suggestions.update(
|
|
61
81
|
{
|
|
62
82
|
"recommended_agent": "crackerjack-architect",
|
|
63
|
-
"reason": "Python project
|
|
64
|
-
"usage": 'Task tool with subagent_type="crackerjack-architect"',
|
|
83
|
+
"reason": "Python project-ensure crackerjack compliance from the start",
|
|
84
|
+
"usage": 'Task tool with subagent_type ="crackerjack-architect"',
|
|
65
85
|
"priority": "MEDIUM",
|
|
66
86
|
}
|
|
67
87
|
)
|
|
@@ -70,7 +90,6 @@ def _suggest_agent_for_context(state_manager) -> dict[str, t.Any]:
|
|
|
70
90
|
|
|
71
91
|
|
|
72
92
|
def _create_error_response(message: str, success: bool = False) -> str:
|
|
73
|
-
"""Utility function to create standardized error responses."""
|
|
74
93
|
import json
|
|
75
94
|
|
|
76
95
|
return json.dumps({"error": message, "success": success})
|
|
@@ -145,7 +164,6 @@ def _add_state_manager_stats(stats: dict, state_manager) -> None:
|
|
|
145
164
|
|
|
146
165
|
|
|
147
166
|
def _get_active_jobs(context) -> list[dict[str, t.Any]]:
|
|
148
|
-
"""Get information about active jobs from progress files."""
|
|
149
167
|
jobs = []
|
|
150
168
|
if not context.progress_dir.exists():
|
|
151
169
|
return jobs
|
|
@@ -174,80 +192,96 @@ def _get_active_jobs(context) -> list[dict[str, t.Any]]:
|
|
|
174
192
|
return jobs
|
|
175
193
|
|
|
176
194
|
|
|
177
|
-
async def
|
|
178
|
-
|
|
195
|
+
async def _get_comprehensive_status_secure(
|
|
196
|
+
client_id: str = "mcp_client",
|
|
197
|
+
client_ip: str = "127.0.0.1",
|
|
198
|
+
auth_header: str | None = None,
|
|
199
|
+
verbosity: StatusVerbosity = StatusVerbosity.STANDARD,
|
|
200
|
+
) -> dict[str, t.Any]:
|
|
201
|
+
"""Get comprehensive status with full security integration."""
|
|
202
|
+
|
|
203
|
+
# 1. Authentication
|
|
204
|
+
auth_manager = get_status_authenticator()
|
|
179
205
|
try:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
206
|
+
credentials = await authenticate_status_request(
|
|
207
|
+
auth_header, client_ip, "get_comprehensive_status"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Check if operation is allowed for this auth level
|
|
211
|
+
if not auth_manager.is_operation_allowed(
|
|
212
|
+
"get_comprehensive_status", credentials.access_level
|
|
213
|
+
):
|
|
214
|
+
return {"error": "Insufficient privileges for comprehensive status"}
|
|
183
215
|
|
|
184
|
-
|
|
185
|
-
return {"error": "
|
|
216
|
+
except Exception as e:
|
|
217
|
+
return {"error": f"Authentication failed: {e}"}
|
|
186
218
|
|
|
219
|
+
# 2. Security validation
|
|
187
220
|
try:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
221
|
+
await validate_status_request(client_id, "get_comprehensive_status", {})
|
|
222
|
+
except Exception as e:
|
|
223
|
+
return {"error": f"Security validation failed: {e}"}
|
|
224
|
+
|
|
225
|
+
# 3. Resource management with bounded operations
|
|
226
|
+
try:
|
|
227
|
+
return await execute_bounded_status_operation(
|
|
228
|
+
"status_collection",
|
|
229
|
+
client_id,
|
|
230
|
+
_collect_comprehensive_status_internal,
|
|
231
|
+
client_id,
|
|
232
|
+
verbosity,
|
|
192
233
|
)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
return {"error": f"Resource limit exceeded: {e}"}
|
|
193
236
|
|
|
194
|
-
mcp_processes = find_mcp_server_processes()
|
|
195
|
-
websocket_processes = find_websocket_server_processes()
|
|
196
237
|
|
|
197
|
-
|
|
198
|
-
|
|
238
|
+
async def _collect_comprehensive_status_internal(
|
|
239
|
+
client_id: str = "mcp_client",
|
|
240
|
+
verbosity: StatusVerbosity = StatusVerbosity.STANDARD,
|
|
241
|
+
) -> dict[str, t.Any]:
|
|
242
|
+
"""Internal comprehensive status collection using thread-safe collector."""
|
|
199
243
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
try:
|
|
203
|
-
websocket_status = await context.get_websocket_server_status()
|
|
204
|
-
except (ConnectionError, TimeoutError) as e:
|
|
205
|
-
websocket_status = {"error": f"Connection failed: {e}"}
|
|
206
|
-
except Exception as e:
|
|
207
|
-
websocket_status = {"error": f"Status unavailable: {e}"}
|
|
244
|
+
# Use thread-safe status collector
|
|
245
|
+
collector = get_thread_safe_status_collector()
|
|
208
246
|
|
|
209
|
-
|
|
247
|
+
try:
|
|
248
|
+
# Collect status with thread safety and proper timeout handling
|
|
249
|
+
snapshot = await collector.collect_comprehensive_status(
|
|
250
|
+
client_id=client_id,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Build final status from snapshot
|
|
210
254
|
status = {
|
|
211
|
-
"services":
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
"
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
"port": getattr(context, "websocket_server_port", 8675),
|
|
220
|
-
"status": websocket_status,
|
|
221
|
-
},
|
|
255
|
+
"services": snapshot.services,
|
|
256
|
+
"jobs": snapshot.jobs,
|
|
257
|
+
"server_stats": snapshot.server_stats,
|
|
258
|
+
"collection_info": {
|
|
259
|
+
"timestamp": snapshot.timestamp,
|
|
260
|
+
"duration": snapshot.collection_duration,
|
|
261
|
+
"is_complete": snapshot.is_complete,
|
|
262
|
+
"errors": snapshot.errors,
|
|
222
263
|
},
|
|
223
|
-
"jobs": {
|
|
224
|
-
"active_count": len(
|
|
225
|
-
[j for j in active_jobs if j["status"] == "running"],
|
|
226
|
-
),
|
|
227
|
-
"completed_count": len(
|
|
228
|
-
[j for j in active_jobs if j["status"] == "completed"],
|
|
229
|
-
),
|
|
230
|
-
"failed_count": len(
|
|
231
|
-
[j for j in active_jobs if j["status"] == "failed"],
|
|
232
|
-
),
|
|
233
|
-
"details": active_jobs,
|
|
234
|
-
},
|
|
235
|
-
"server_stats": _build_server_stats(context),
|
|
236
|
-
"timestamp": time.time(),
|
|
237
264
|
}
|
|
238
265
|
|
|
239
|
-
# Add
|
|
240
|
-
|
|
241
|
-
|
|
266
|
+
# Add agent suggestions if available
|
|
267
|
+
context = None
|
|
268
|
+
with suppress(RuntimeError):
|
|
269
|
+
context = get_context()
|
|
242
270
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
271
|
+
if context:
|
|
272
|
+
state_manager = getattr(context, "state_manager", None)
|
|
273
|
+
if state_manager:
|
|
274
|
+
status["agent_suggestions"] = _suggest_agent_for_context(state_manager)
|
|
246
275
|
|
|
247
276
|
return status
|
|
248
277
|
|
|
249
278
|
except Exception as e:
|
|
250
|
-
return {"error": f"Failed to
|
|
279
|
+
return {"error": f"Failed to collect comprehensive status: {e}"}
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
async def _get_comprehensive_status() -> dict[str, t.Any]:
|
|
283
|
+
"""Legacy wrapper for backward compatibility."""
|
|
284
|
+
return await _get_comprehensive_status_secure()
|
|
251
285
|
|
|
252
286
|
|
|
253
287
|
def register_monitoring_tools(mcp_app: t.Any) -> None:
|
|
@@ -262,20 +296,27 @@ def register_monitoring_tools(mcp_app: t.Any) -> None:
|
|
|
262
296
|
def _register_stage_status_tool(mcp_app: t.Any) -> None:
|
|
263
297
|
@mcp_app.tool()
|
|
264
298
|
async def get_stage_status() -> str:
|
|
265
|
-
|
|
266
|
-
if not context:
|
|
267
|
-
return _create_error_response("Server context not available")
|
|
299
|
+
client_id = "mcp_client"
|
|
268
300
|
|
|
269
301
|
try:
|
|
302
|
+
# Security validation
|
|
303
|
+
await validate_status_request(client_id, "get_stage_status", {})
|
|
304
|
+
|
|
305
|
+
context = get_context()
|
|
306
|
+
if not context:
|
|
307
|
+
return _create_error_response("Server context not available")
|
|
308
|
+
|
|
270
309
|
state_manager = getattr(context, "state_manager", None)
|
|
271
310
|
if not state_manager:
|
|
272
311
|
return _create_error_response("State manager not available")
|
|
273
312
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
"
|
|
277
|
-
|
|
278
|
-
|
|
313
|
+
# Use bounded operation for resource protection
|
|
314
|
+
result = await execute_bounded_status_operation(
|
|
315
|
+
"stage_status",
|
|
316
|
+
client_id,
|
|
317
|
+
_build_stage_status,
|
|
318
|
+
state_manager,
|
|
319
|
+
)
|
|
279
320
|
|
|
280
321
|
return json.dumps(result, indent=2)
|
|
281
322
|
|
|
@@ -283,19 +324,40 @@ def _register_stage_status_tool(mcp_app: t.Any) -> None:
|
|
|
283
324
|
return f'{{"error": "Failed to get stage status: {e}"}}'
|
|
284
325
|
|
|
285
326
|
|
|
327
|
+
def _build_stage_status(state_manager) -> dict[str, t.Any]:
|
|
328
|
+
"""Build stage status with bounded resource usage."""
|
|
329
|
+
return {
|
|
330
|
+
"stages": _get_stage_status_dict(state_manager),
|
|
331
|
+
"session": _get_session_info(state_manager),
|
|
332
|
+
"timestamp": time.time(),
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
|
|
286
336
|
def _register_next_action_tool(mcp_app: t.Any) -> None:
|
|
287
337
|
@mcp_app.tool()
|
|
288
338
|
async def get_next_action() -> str:
|
|
289
|
-
|
|
290
|
-
if not context:
|
|
291
|
-
return _create_error_response("Server context not available")
|
|
339
|
+
client_id = "mcp_client"
|
|
292
340
|
|
|
293
341
|
try:
|
|
342
|
+
# Security validation
|
|
343
|
+
await validate_status_request(client_id, "get_next_action", {})
|
|
344
|
+
|
|
345
|
+
context = get_context()
|
|
346
|
+
if not context:
|
|
347
|
+
return _create_error_response("Server context not available")
|
|
348
|
+
|
|
294
349
|
state_manager = getattr(context, "state_manager", None)
|
|
295
350
|
if not state_manager:
|
|
296
351
|
return '{"recommended_action": "initialize", "reason": "No state manager available"}'
|
|
297
352
|
|
|
298
|
-
|
|
353
|
+
# Use bounded operation for consistency
|
|
354
|
+
action = await execute_bounded_status_operation(
|
|
355
|
+
"next_action",
|
|
356
|
+
client_id,
|
|
357
|
+
_determine_next_action,
|
|
358
|
+
state_manager,
|
|
359
|
+
)
|
|
360
|
+
|
|
299
361
|
return json.dumps(action, indent=2)
|
|
300
362
|
|
|
301
363
|
except Exception as e:
|
|
@@ -305,53 +367,122 @@ def _register_next_action_tool(mcp_app: t.Any) -> None:
|
|
|
305
367
|
def _register_server_stats_tool(mcp_app: t.Any) -> None:
|
|
306
368
|
@mcp_app.tool()
|
|
307
369
|
async def get_server_stats() -> str:
|
|
308
|
-
|
|
309
|
-
if not context:
|
|
310
|
-
return _create_error_response("Server context not available")
|
|
370
|
+
client_id = "mcp_client"
|
|
311
371
|
|
|
312
372
|
try:
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
373
|
+
# Security validation
|
|
374
|
+
await validate_status_request(client_id, "get_server_stats", {})
|
|
375
|
+
|
|
376
|
+
# Use secure status operation with resource limits
|
|
377
|
+
async with await secure_status_operation(
|
|
378
|
+
client_id, "get_server_stats", timeout=15.0
|
|
379
|
+
):
|
|
380
|
+
# Get context
|
|
381
|
+
context = get_context()
|
|
382
|
+
if not context:
|
|
383
|
+
formatter = get_secure_status_formatter()
|
|
384
|
+
error_response = formatter.format_error_response(
|
|
385
|
+
"Server context not available",
|
|
386
|
+
)
|
|
387
|
+
return json.dumps(error_response, indent=2)
|
|
388
|
+
|
|
389
|
+
# Get raw stats with bounded operation
|
|
390
|
+
raw_stats = await execute_bounded_status_operation(
|
|
391
|
+
"server_stats",
|
|
392
|
+
client_id,
|
|
393
|
+
_build_server_stats_secure,
|
|
394
|
+
context,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
# Apply secure formatting
|
|
398
|
+
secure_stats = format_secure_status(
|
|
399
|
+
raw_stats,
|
|
400
|
+
project_root=context.config.project_path,
|
|
401
|
+
user_context="mcp_client",
|
|
402
|
+
)
|
|
316
403
|
|
|
317
|
-
|
|
404
|
+
return json.dumps(secure_stats, indent=2)
|
|
318
405
|
|
|
319
406
|
except Exception as e:
|
|
320
|
-
|
|
407
|
+
# Use secure error formatting
|
|
408
|
+
formatter = get_secure_status_formatter()
|
|
409
|
+
error_response = formatter.format_error_response(
|
|
410
|
+
str(e),
|
|
411
|
+
)
|
|
412
|
+
return json.dumps(error_response, indent=2)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _build_server_stats_secure(context) -> dict[str, t.Any]:
|
|
416
|
+
"""Build server stats with security controls."""
|
|
417
|
+
|
|
418
|
+
# Build base stats
|
|
419
|
+
stats = _build_server_stats(context)
|
|
420
|
+
|
|
421
|
+
# Add state manager stats
|
|
422
|
+
state_manager = getattr(context, "state_manager", None)
|
|
423
|
+
_add_state_manager_stats(stats, state_manager)
|
|
424
|
+
|
|
425
|
+
# Add security status
|
|
426
|
+
security_manager = get_status_security_manager()
|
|
427
|
+
stats["security_status"] = security_manager.get_security_status()
|
|
428
|
+
|
|
429
|
+
# Add resource limiter status if available
|
|
430
|
+
with suppress(Exception):
|
|
431
|
+
# Resource limiter may not be initialized
|
|
432
|
+
resource_limiter = get_websocket_resource_limiter()
|
|
433
|
+
stats["websocket_resources"] = resource_limiter.get_resource_status()
|
|
434
|
+
|
|
435
|
+
return stats
|
|
321
436
|
|
|
322
437
|
|
|
323
438
|
def _register_comprehensive_status_tool(mcp_app: t.Any) -> None:
|
|
324
439
|
@mcp_app.tool()
|
|
325
440
|
async def get_comprehensive_status() -> str:
|
|
326
|
-
""
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
Provides information about:
|
|
330
|
-
- MCP server status and processes
|
|
331
|
-
- WebSocket server status and connections
|
|
332
|
-
- Active, completed, and failed jobs
|
|
333
|
-
- Progress information for running jobs
|
|
334
|
-
- Error metrics and resolution counts
|
|
335
|
-
- Service health and resource usage
|
|
336
|
-
"""
|
|
441
|
+
client_id = "mcp_client"
|
|
442
|
+
client_ip = "127.0.0.1"
|
|
443
|
+
|
|
337
444
|
try:
|
|
338
|
-
status
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
445
|
+
# Use secure status operation with request lock
|
|
446
|
+
async with await secure_status_operation(
|
|
447
|
+
client_id,
|
|
448
|
+
"get_comprehensive_status",
|
|
449
|
+
):
|
|
450
|
+
# Get raw status data with full security integration
|
|
451
|
+
raw_status = await _get_comprehensive_status_secure(
|
|
452
|
+
client_id=client_id,
|
|
453
|
+
client_ip=client_ip,
|
|
454
|
+
# Local-only access (auth_header defaults to None)
|
|
455
|
+
)
|
|
342
456
|
|
|
343
|
-
|
|
457
|
+
# Apply secure formatting
|
|
458
|
+
context = get_context()
|
|
459
|
+
project_root = context.config.project_path if context else None
|
|
460
|
+
|
|
461
|
+
secure_status = format_secure_status(
|
|
462
|
+
raw_status,
|
|
463
|
+
project_root=project_root,
|
|
464
|
+
user_context="mcp_client",
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
return json.dumps(secure_status, indent=2)
|
|
468
|
+
|
|
469
|
+
except Exception as e:
|
|
470
|
+
# Use secure error formatting
|
|
471
|
+
formatter = get_secure_status_formatter()
|
|
472
|
+
error_response = formatter.format_error_response(
|
|
473
|
+
str(e),
|
|
474
|
+
)
|
|
475
|
+
return json.dumps(error_response, indent=2)
|
|
344
476
|
|
|
345
477
|
|
|
346
478
|
def _register_command_help_tool(mcp_app: t.Any) -> None:
|
|
347
479
|
@mcp_app.tool()
|
|
348
480
|
async def list_slash_commands() -> str:
|
|
349
|
-
"""List all available crackerjack slash commands with descriptions and usage."""
|
|
350
481
|
try:
|
|
351
482
|
commands = {
|
|
352
|
-
"/crackerjack:run": {
|
|
483
|
+
"/ crackerjack: run": {
|
|
353
484
|
"description": "Run full iterative auto-fixing with AI agent, tests, progress tracking, and verbose output",
|
|
354
|
-
"usage": "Direct execution
|
|
485
|
+
"usage": "Direct execution-no parameters needed",
|
|
355
486
|
"features": [
|
|
356
487
|
"Up to 10 iterations of quality improvement",
|
|
357
488
|
"Real-time WebSocket progress streaming",
|
|
@@ -360,9 +491,9 @@ def _register_command_help_tool(mcp_app: t.Any) -> None:
|
|
|
360
491
|
"Debug mode support",
|
|
361
492
|
],
|
|
362
493
|
},
|
|
363
|
-
"/crackerjack:status": {
|
|
494
|
+
"/ crackerjack: status": {
|
|
364
495
|
"description": "Get comprehensive system status including servers, jobs, and resource usage",
|
|
365
|
-
"usage": "Direct execution
|
|
496
|
+
"usage": "Direct execution-no parameters needed",
|
|
366
497
|
"features": [
|
|
367
498
|
"MCP server health monitoring",
|
|
368
499
|
"WebSocket server status",
|
|
@@ -371,11 +502,11 @@ def _register_command_help_tool(mcp_app: t.Any) -> None:
|
|
|
371
502
|
"Error counts and progress data",
|
|
372
503
|
],
|
|
373
504
|
},
|
|
374
|
-
"/crackerjack:init": {
|
|
505
|
+
"/ crackerjack: init": {
|
|
375
506
|
"description": "Initialize or update crackerjack configuration with smart configuration merging",
|
|
376
507
|
"usage": "Optional parameters: target_path (defaults to current directory)",
|
|
377
508
|
"kwargs": {
|
|
378
|
-
"force": "boolean
|
|
509
|
+
"force": "boolean-force overwrite existing configurations"
|
|
379
510
|
},
|
|
380
511
|
"features": [
|
|
381
512
|
"Smart merge preserving existing configurations",
|
|
@@ -404,9 +535,8 @@ def _register_command_help_tool(mcp_app: t.Any) -> None:
|
|
|
404
535
|
|
|
405
536
|
|
|
406
537
|
def _validate_status_components(components: str) -> tuple[set[str], str | None]:
|
|
407
|
-
"""Validate and parse status components."""
|
|
408
538
|
valid_components = {"services", "jobs", "resources", "all"}
|
|
409
|
-
requested = {c.strip().lower() for c in components.split(",")}
|
|
539
|
+
requested = {c.strip().lower() for c in components.split(", ")}
|
|
410
540
|
|
|
411
541
|
invalid = requested - valid_components
|
|
412
542
|
if invalid:
|
|
@@ -416,7 +546,6 @@ def _validate_status_components(components: str) -> tuple[set[str], str | None]:
|
|
|
416
546
|
|
|
417
547
|
|
|
418
548
|
def _get_services_status() -> dict:
|
|
419
|
-
"""Get services status information."""
|
|
420
549
|
from crackerjack.services.server_manager import (
|
|
421
550
|
find_mcp_server_processes,
|
|
422
551
|
find_websocket_server_processes,
|
|
@@ -438,7 +567,6 @@ def _get_services_status() -> dict:
|
|
|
438
567
|
|
|
439
568
|
|
|
440
569
|
def _get_resources_status(context: t.Any) -> dict:
|
|
441
|
-
"""Get resources status information."""
|
|
442
570
|
temp_files_count = (
|
|
443
571
|
len(list(context.progress_dir.glob("*.json")))
|
|
444
572
|
if context.progress_dir.exists()
|
|
@@ -452,7 +580,6 @@ def _get_resources_status(context: t.Any) -> dict:
|
|
|
452
580
|
|
|
453
581
|
|
|
454
582
|
def _build_filtered_status(requested: set[str], context: t.Any) -> dict:
|
|
455
|
-
"""Build filtered status based on requested components."""
|
|
456
583
|
filtered_status = {"timestamp": time.time()}
|
|
457
584
|
|
|
458
585
|
if "services" in requested:
|
|
@@ -470,33 +597,69 @@ def _build_filtered_status(requested: set[str], context: t.Any) -> dict:
|
|
|
470
597
|
def _register_filtered_status_tool(mcp_app: t.Any) -> None:
|
|
471
598
|
@mcp_app.tool()
|
|
472
599
|
async def get_filtered_status(components: str = "all") -> str:
|
|
473
|
-
""
|
|
600
|
+
client_id = "mcp_client"
|
|
474
601
|
|
|
475
|
-
Args:
|
|
476
|
-
components: Comma-separated list of components to include:
|
|
477
|
-
'services', 'jobs', 'resources', 'all' (default)
|
|
478
|
-
"""
|
|
479
602
|
try:
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
603
|
+
# Security validation with component filter data
|
|
604
|
+
await validate_status_request(
|
|
605
|
+
client_id, "get_filtered_status", {"components": components}
|
|
606
|
+
)
|
|
483
607
|
|
|
484
|
-
|
|
485
|
-
if "all" in requested:
|
|
486
|
-
status = await _get_comprehensive_status()
|
|
487
|
-
return json.dumps(status, indent=2)
|
|
608
|
+
return await _process_filtered_status_request(client_id, components)
|
|
488
609
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
return json.dumps(
|
|
492
|
-
{"error": "Server context not available", "success": False}
|
|
493
|
-
)
|
|
610
|
+
except Exception as e:
|
|
611
|
+
return _format_status_error(str(e))
|
|
494
612
|
|
|
495
|
-
filtered_status = _build_filtered_status(requested, context)
|
|
496
|
-
return json.dumps(filtered_status, indent=2)
|
|
497
613
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
614
|
+
async def _process_filtered_status_request(client_id: str, components: str) -> str:
|
|
615
|
+
"""Process filtered status request with component validation."""
|
|
616
|
+
requested, error = _validate_status_components(components)
|
|
617
|
+
if error:
|
|
618
|
+
return _format_status_error(error)
|
|
619
|
+
|
|
620
|
+
# Use secure status operation with timeout
|
|
621
|
+
async with await secure_status_operation(
|
|
622
|
+
client_id, "get_filtered_status", timeout=20.0
|
|
623
|
+
):
|
|
624
|
+
return await _collect_status_data(client_id, requested)
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
async def _collect_status_data(client_id: str, requested: set[str]) -> str:
|
|
628
|
+
"""Collect status data based on requested components."""
|
|
629
|
+
context = get_context()
|
|
630
|
+
|
|
631
|
+
if "all" in requested:
|
|
632
|
+
raw_status = await _get_comprehensive_status_secure(
|
|
633
|
+
client_id=client_id,
|
|
634
|
+
)
|
|
635
|
+
else:
|
|
636
|
+
if not context:
|
|
637
|
+
return _format_status_error("Server context not available")
|
|
638
|
+
|
|
639
|
+
# Use bounded operation for filtered status
|
|
640
|
+
raw_status = await execute_bounded_status_operation(
|
|
641
|
+
"filtered_status",
|
|
642
|
+
client_id,
|
|
643
|
+
_build_filtered_status,
|
|
644
|
+
requested,
|
|
645
|
+
context,
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
return _apply_secure_formatting(raw_status, context)
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def _apply_secure_formatting(raw_status: dict, context: t.Any) -> str:
|
|
652
|
+
"""Apply secure formatting to status data."""
|
|
653
|
+
project_root = context.config.project_path if context else None
|
|
654
|
+
secure_status = format_secure_status(
|
|
655
|
+
raw_status,
|
|
656
|
+
project_root=project_root,
|
|
657
|
+
user_context="mcp_client",
|
|
658
|
+
)
|
|
659
|
+
return json.dumps(secure_status, indent=2)
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def _format_status_error(error_message: str) -> str:
|
|
663
|
+
"""Format status error response consistently."""
|
|
664
|
+
formatter = get_secure_status_formatter()
|
|
665
|
+
return json.dumps(formatter.format_error_response(error_message), indent=2)
|