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
|
@@ -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
|
|
@@ -29,7 +29,7 @@ from crackerjack.services.websocket_resource_limiter import (
|
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def _suggest_agent_for_context(state_manager) -> dict[str, t.Any]:
|
|
32
|
+
def _suggest_agent_for_context(state_manager: t.Any) -> dict[str, t.Any]:
|
|
33
33
|
suggestions = {
|
|
34
34
|
"recommended_agent": None,
|
|
35
35
|
"reason": "",
|
|
@@ -95,12 +95,12 @@ def _create_error_response(message: str, success: bool = False) -> str:
|
|
|
95
95
|
return json.dumps({"error": message, "success": success})
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def _get_stage_status_dict(state_manager) -> dict[str, str]:
|
|
98
|
+
def _get_stage_status_dict(state_manager: t.Any) -> dict[str, str]:
|
|
99
99
|
stages = ["fast", "comprehensive", "tests", "cleaning"]
|
|
100
100
|
return {stage: state_manager.get_stage_status(stage) for stage in stages}
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
def _get_session_info(state_manager) -> dict[str, t.Any]:
|
|
103
|
+
def _get_session_info(state_manager: t.Any) -> dict[str, t.Any]:
|
|
104
104
|
return {
|
|
105
105
|
"total_iterations": getattr(state_manager, "iteration_count", 0),
|
|
106
106
|
"current_iteration": getattr(state_manager, "current_iteration", 0),
|
|
@@ -108,7 +108,7 @@ def _get_session_info(state_manager) -> dict[str, t.Any]:
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
def _determine_next_action(state_manager) -> dict[str, t.Any]:
|
|
111
|
+
async def _determine_next_action(state_manager: t.Any) -> dict[str, t.Any]:
|
|
112
112
|
stage_priorities = [
|
|
113
113
|
("fast", "Fast hooks not completed"),
|
|
114
114
|
("tests", "Tests not completed"),
|
|
@@ -130,7 +130,7 @@ def _determine_next_action(state_manager) -> dict[str, t.Any]:
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
|
|
133
|
-
def _build_server_stats(context) -> dict[str, t.Any]:
|
|
133
|
+
async def _build_server_stats(context: t.Any) -> dict[str, t.Any]:
|
|
134
134
|
return {
|
|
135
135
|
"server_info": {
|
|
136
136
|
"project_path": str(context.config.project_path),
|
|
@@ -145,7 +145,7 @@ def _build_server_stats(context) -> dict[str, t.Any]:
|
|
|
145
145
|
else None,
|
|
146
146
|
},
|
|
147
147
|
"resource_usage": {
|
|
148
|
-
"temp_files_count": len(list(context.progress_dir.glob("*.json")))
|
|
148
|
+
"temp_files_count": len(list[t.Any](context.progress_dir.glob("*.json")))
|
|
149
149
|
if context.progress_dir.exists()
|
|
150
150
|
else 0,
|
|
151
151
|
"progress_dir": str(context.progress_dir),
|
|
@@ -154,7 +154,7 @@ def _build_server_stats(context) -> dict[str, t.Any]:
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
def _add_state_manager_stats(stats: dict, state_manager) -> None:
|
|
157
|
+
def _add_state_manager_stats(stats: dict[str, t.Any], state_manager: t.Any) -> None:
|
|
158
158
|
if state_manager:
|
|
159
159
|
stats["state_manager"] = {
|
|
160
160
|
"iteration_count": getattr(state_manager, "iteration_count", 0),
|
|
@@ -163,8 +163,8 @@ def _add_state_manager_stats(stats: dict, state_manager) -> None:
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
|
|
166
|
-
def _get_active_jobs(context) -> list[dict[str, t.Any]]:
|
|
167
|
-
jobs = []
|
|
166
|
+
def _get_active_jobs(context: t.Any) -> list[dict[str, t.Any]]:
|
|
167
|
+
jobs: list[dict[str, t.Any]] = []
|
|
168
168
|
if not context.progress_dir.exists():
|
|
169
169
|
return jobs
|
|
170
170
|
|
|
@@ -198,16 +198,12 @@ async def _get_comprehensive_status_secure(
|
|
|
198
198
|
auth_header: str | None = None,
|
|
199
199
|
verbosity: StatusVerbosity = StatusVerbosity.STANDARD,
|
|
200
200
|
) -> dict[str, t.Any]:
|
|
201
|
-
"""Get comprehensive status with full security integration."""
|
|
202
|
-
|
|
203
|
-
# 1. Authentication
|
|
204
201
|
auth_manager = get_status_authenticator()
|
|
205
202
|
try:
|
|
206
203
|
credentials = await authenticate_status_request(
|
|
207
204
|
auth_header, client_ip, "get_comprehensive_status"
|
|
208
205
|
)
|
|
209
206
|
|
|
210
|
-
# Check if operation is allowed for this auth level
|
|
211
207
|
if not auth_manager.is_operation_allowed(
|
|
212
208
|
"get_comprehensive_status", credentials.access_level
|
|
213
209
|
):
|
|
@@ -216,21 +212,20 @@ async def _get_comprehensive_status_secure(
|
|
|
216
212
|
except Exception as e:
|
|
217
213
|
return {"error": f"Authentication failed: {e}"}
|
|
218
214
|
|
|
219
|
-
# 2. Security validation
|
|
220
215
|
try:
|
|
221
216
|
await validate_status_request(client_id, "get_comprehensive_status", {})
|
|
222
217
|
except Exception as e:
|
|
223
218
|
return {"error": f"Security validation failed: {e}"}
|
|
224
219
|
|
|
225
|
-
# 3. Resource management with bounded operations
|
|
226
220
|
try:
|
|
227
|
-
|
|
221
|
+
result: dict[str, t.Any] = await execute_bounded_status_operation(
|
|
228
222
|
"status_collection",
|
|
229
223
|
client_id,
|
|
230
224
|
_collect_comprehensive_status_internal,
|
|
231
225
|
client_id,
|
|
232
226
|
verbosity,
|
|
233
227
|
)
|
|
228
|
+
return result
|
|
234
229
|
except Exception as e:
|
|
235
230
|
return {"error": f"Resource limit exceeded: {e}"}
|
|
236
231
|
|
|
@@ -239,18 +234,13 @@ async def _collect_comprehensive_status_internal(
|
|
|
239
234
|
client_id: str = "mcp_client",
|
|
240
235
|
verbosity: StatusVerbosity = StatusVerbosity.STANDARD,
|
|
241
236
|
) -> dict[str, t.Any]:
|
|
242
|
-
"""Internal comprehensive status collection using thread-safe collector."""
|
|
243
|
-
|
|
244
|
-
# Use thread-safe status collector
|
|
245
237
|
collector = get_thread_safe_status_collector()
|
|
246
238
|
|
|
247
239
|
try:
|
|
248
|
-
# Collect status with thread safety and proper timeout handling
|
|
249
240
|
snapshot = await collector.collect_comprehensive_status(
|
|
250
241
|
client_id=client_id,
|
|
251
242
|
)
|
|
252
243
|
|
|
253
|
-
# Build final status from snapshot
|
|
254
244
|
status = {
|
|
255
245
|
"services": snapshot.services,
|
|
256
246
|
"jobs": snapshot.jobs,
|
|
@@ -263,7 +253,6 @@ async def _collect_comprehensive_status_internal(
|
|
|
263
253
|
},
|
|
264
254
|
}
|
|
265
255
|
|
|
266
|
-
# Add agent suggestions if available
|
|
267
256
|
context = None
|
|
268
257
|
with suppress(RuntimeError):
|
|
269
258
|
context = get_context()
|
|
@@ -280,7 +269,6 @@ async def _collect_comprehensive_status_internal(
|
|
|
280
269
|
|
|
281
270
|
|
|
282
271
|
async def _get_comprehensive_status() -> dict[str, t.Any]:
|
|
283
|
-
"""Legacy wrapper for backward compatibility."""
|
|
284
272
|
return await _get_comprehensive_status_secure()
|
|
285
273
|
|
|
286
274
|
|
|
@@ -294,12 +282,11 @@ def register_monitoring_tools(mcp_app: t.Any) -> None:
|
|
|
294
282
|
|
|
295
283
|
|
|
296
284
|
def _register_stage_status_tool(mcp_app: t.Any) -> None:
|
|
297
|
-
@mcp_app.tool()
|
|
285
|
+
@mcp_app.tool() # type: ignore[misc] # type: ignore[misc]
|
|
298
286
|
async def get_stage_status() -> str:
|
|
299
287
|
client_id = "mcp_client"
|
|
300
288
|
|
|
301
289
|
try:
|
|
302
|
-
# Security validation
|
|
303
290
|
await validate_status_request(client_id, "get_stage_status", {})
|
|
304
291
|
|
|
305
292
|
context = get_context()
|
|
@@ -310,7 +297,6 @@ def _register_stage_status_tool(mcp_app: t.Any) -> None:
|
|
|
310
297
|
if not state_manager:
|
|
311
298
|
return _create_error_response("State manager not available")
|
|
312
299
|
|
|
313
|
-
# Use bounded operation for resource protection
|
|
314
300
|
result = await execute_bounded_status_operation(
|
|
315
301
|
"stage_status",
|
|
316
302
|
client_id,
|
|
@@ -324,8 +310,7 @@ def _register_stage_status_tool(mcp_app: t.Any) -> None:
|
|
|
324
310
|
return f'{{"error": "Failed to get stage status: {e}"}}'
|
|
325
311
|
|
|
326
312
|
|
|
327
|
-
def _build_stage_status(state_manager) -> dict[str, t.Any]:
|
|
328
|
-
"""Build stage status with bounded resource usage."""
|
|
313
|
+
async def _build_stage_status(state_manager: t.Any) -> dict[str, t.Any]:
|
|
329
314
|
return {
|
|
330
315
|
"stages": _get_stage_status_dict(state_manager),
|
|
331
316
|
"session": _get_session_info(state_manager),
|
|
@@ -334,12 +319,11 @@ def _build_stage_status(state_manager) -> dict[str, t.Any]:
|
|
|
334
319
|
|
|
335
320
|
|
|
336
321
|
def _register_next_action_tool(mcp_app: t.Any) -> None:
|
|
337
|
-
@mcp_app.tool()
|
|
322
|
+
@mcp_app.tool() # type: ignore[misc] # type: ignore[misc]
|
|
338
323
|
async def get_next_action() -> str:
|
|
339
324
|
client_id = "mcp_client"
|
|
340
325
|
|
|
341
326
|
try:
|
|
342
|
-
# Security validation
|
|
343
327
|
await validate_status_request(client_id, "get_next_action", {})
|
|
344
328
|
|
|
345
329
|
context = get_context()
|
|
@@ -350,7 +334,6 @@ def _register_next_action_tool(mcp_app: t.Any) -> None:
|
|
|
350
334
|
if not state_manager:
|
|
351
335
|
return '{"recommended_action": "initialize", "reason": "No state manager available"}'
|
|
352
336
|
|
|
353
|
-
# Use bounded operation for consistency
|
|
354
337
|
action = await execute_bounded_status_operation(
|
|
355
338
|
"next_action",
|
|
356
339
|
client_id,
|
|
@@ -365,19 +348,16 @@ def _register_next_action_tool(mcp_app: t.Any) -> None:
|
|
|
365
348
|
|
|
366
349
|
|
|
367
350
|
def _register_server_stats_tool(mcp_app: t.Any) -> None:
|
|
368
|
-
@mcp_app.tool()
|
|
351
|
+
@mcp_app.tool() # type: ignore[misc] # type: ignore[misc]
|
|
369
352
|
async def get_server_stats() -> str:
|
|
370
353
|
client_id = "mcp_client"
|
|
371
354
|
|
|
372
355
|
try:
|
|
373
|
-
# Security validation
|
|
374
356
|
await validate_status_request(client_id, "get_server_stats", {})
|
|
375
357
|
|
|
376
|
-
# Use secure status operation with resource limits
|
|
377
358
|
async with await secure_status_operation(
|
|
378
359
|
client_id, "get_server_stats", timeout=15.0
|
|
379
360
|
):
|
|
380
|
-
# Get context
|
|
381
361
|
context = get_context()
|
|
382
362
|
if not context:
|
|
383
363
|
formatter = get_secure_status_formatter()
|
|
@@ -386,7 +366,6 @@ def _register_server_stats_tool(mcp_app: t.Any) -> None:
|
|
|
386
366
|
)
|
|
387
367
|
return json.dumps(error_response, indent=2)
|
|
388
368
|
|
|
389
|
-
# Get raw stats with bounded operation
|
|
390
369
|
raw_stats = await execute_bounded_status_operation(
|
|
391
370
|
"server_stats",
|
|
392
371
|
client_id,
|
|
@@ -394,7 +373,6 @@ def _register_server_stats_tool(mcp_app: t.Any) -> None:
|
|
|
394
373
|
context,
|
|
395
374
|
)
|
|
396
375
|
|
|
397
|
-
# Apply secure formatting
|
|
398
376
|
secure_stats = format_secure_status(
|
|
399
377
|
raw_stats,
|
|
400
378
|
project_root=context.config.project_path,
|
|
@@ -404,7 +382,6 @@ def _register_server_stats_tool(mcp_app: t.Any) -> None:
|
|
|
404
382
|
return json.dumps(secure_stats, indent=2)
|
|
405
383
|
|
|
406
384
|
except Exception as e:
|
|
407
|
-
# Use secure error formatting
|
|
408
385
|
formatter = get_secure_status_formatter()
|
|
409
386
|
error_response = formatter.format_error_response(
|
|
410
387
|
str(e),
|
|
@@ -412,23 +389,16 @@ def _register_server_stats_tool(mcp_app: t.Any) -> None:
|
|
|
412
389
|
return json.dumps(error_response, indent=2)
|
|
413
390
|
|
|
414
391
|
|
|
415
|
-
def _build_server_stats_secure(context) -> dict[str, t.Any]:
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
# Build base stats
|
|
419
|
-
stats = _build_server_stats(context)
|
|
392
|
+
async def _build_server_stats_secure(context: t.Any) -> dict[str, t.Any]:
|
|
393
|
+
stats = await _build_server_stats(context)
|
|
420
394
|
|
|
421
|
-
# Add state manager stats
|
|
422
395
|
state_manager = getattr(context, "state_manager", None)
|
|
423
396
|
_add_state_manager_stats(stats, state_manager)
|
|
424
397
|
|
|
425
|
-
# Add security status
|
|
426
398
|
security_manager = get_status_security_manager()
|
|
427
399
|
stats["security_status"] = security_manager.get_security_status()
|
|
428
400
|
|
|
429
|
-
# Add resource limiter status if available
|
|
430
401
|
with suppress(Exception):
|
|
431
|
-
# Resource limiter may not be initialized
|
|
432
402
|
resource_limiter = get_websocket_resource_limiter()
|
|
433
403
|
stats["websocket_resources"] = resource_limiter.get_resource_status()
|
|
434
404
|
|
|
@@ -436,25 +406,21 @@ def _build_server_stats_secure(context) -> dict[str, t.Any]:
|
|
|
436
406
|
|
|
437
407
|
|
|
438
408
|
def _register_comprehensive_status_tool(mcp_app: t.Any) -> None:
|
|
439
|
-
@mcp_app.tool()
|
|
409
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
440
410
|
async def get_comprehensive_status() -> str:
|
|
441
411
|
client_id = "mcp_client"
|
|
442
412
|
client_ip = "127.0.0.1"
|
|
443
413
|
|
|
444
414
|
try:
|
|
445
|
-
# Use secure status operation with request lock
|
|
446
415
|
async with await secure_status_operation(
|
|
447
416
|
client_id,
|
|
448
417
|
"get_comprehensive_status",
|
|
449
418
|
):
|
|
450
|
-
# Get raw status data with full security integration
|
|
451
419
|
raw_status = await _get_comprehensive_status_secure(
|
|
452
420
|
client_id=client_id,
|
|
453
421
|
client_ip=client_ip,
|
|
454
|
-
# Local-only access (auth_header defaults to None)
|
|
455
422
|
)
|
|
456
423
|
|
|
457
|
-
# Apply secure formatting
|
|
458
424
|
context = get_context()
|
|
459
425
|
project_root = context.config.project_path if context else None
|
|
460
426
|
|
|
@@ -467,7 +433,6 @@ def _register_comprehensive_status_tool(mcp_app: t.Any) -> None:
|
|
|
467
433
|
return json.dumps(secure_status, indent=2)
|
|
468
434
|
|
|
469
435
|
except Exception as e:
|
|
470
|
-
# Use secure error formatting
|
|
471
436
|
formatter = get_secure_status_formatter()
|
|
472
437
|
error_response = formatter.format_error_response(
|
|
473
438
|
str(e),
|
|
@@ -476,7 +441,7 @@ def _register_comprehensive_status_tool(mcp_app: t.Any) -> None:
|
|
|
476
441
|
|
|
477
442
|
|
|
478
443
|
def _register_command_help_tool(mcp_app: t.Any) -> None:
|
|
479
|
-
@mcp_app.tool()
|
|
444
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
480
445
|
async def list_slash_commands() -> str:
|
|
481
446
|
try:
|
|
482
447
|
commands = {
|
|
@@ -520,7 +485,7 @@ def _register_command_help_tool(mcp_app: t.Any) -> None:
|
|
|
520
485
|
|
|
521
486
|
return json.dumps(
|
|
522
487
|
{
|
|
523
|
-
"available_commands": list(commands.keys()),
|
|
488
|
+
"available_commands": list[t.Any](commands.keys()),
|
|
524
489
|
"command_details": commands,
|
|
525
490
|
"total_commands": len(commands),
|
|
526
491
|
},
|
|
@@ -529,7 +494,10 @@ def _register_command_help_tool(mcp_app: t.Any) -> None:
|
|
|
529
494
|
|
|
530
495
|
except Exception as e:
|
|
531
496
|
return json.dumps(
|
|
532
|
-
{
|
|
497
|
+
{
|
|
498
|
+
"error": f"Failed to list[t.Any] slash commands: {e}",
|
|
499
|
+
"success": False,
|
|
500
|
+
},
|
|
533
501
|
indent=2,
|
|
534
502
|
)
|
|
535
503
|
|
|
@@ -545,7 +513,7 @@ def _validate_status_components(components: str) -> tuple[set[str], str | None]:
|
|
|
545
513
|
return requested, None
|
|
546
514
|
|
|
547
515
|
|
|
548
|
-
def _get_services_status() -> dict:
|
|
516
|
+
def _get_services_status() -> dict[str, t.Any]:
|
|
549
517
|
from crackerjack.services.server_manager import (
|
|
550
518
|
find_mcp_server_processes,
|
|
551
519
|
find_websocket_server_processes,
|
|
@@ -566,9 +534,9 @@ def _get_services_status() -> dict:
|
|
|
566
534
|
}
|
|
567
535
|
|
|
568
536
|
|
|
569
|
-
def _get_resources_status(context: t.Any) -> dict:
|
|
537
|
+
def _get_resources_status(context: t.Any) -> dict[str, t.Any]:
|
|
570
538
|
temp_files_count = (
|
|
571
|
-
len(list(context.progress_dir.glob("*.json")))
|
|
539
|
+
len(list[t.Any](context.progress_dir.glob("*.json")))
|
|
572
540
|
if context.progress_dir.exists()
|
|
573
541
|
else 0
|
|
574
542
|
)
|
|
@@ -579,8 +547,10 @@ def _get_resources_status(context: t.Any) -> dict:
|
|
|
579
547
|
}
|
|
580
548
|
|
|
581
549
|
|
|
582
|
-
def _build_filtered_status(
|
|
583
|
-
|
|
550
|
+
async def _build_filtered_status(
|
|
551
|
+
requested: set[str], context: t.Any
|
|
552
|
+
) -> dict[str, t.Any]:
|
|
553
|
+
filtered_status: dict[str, t.Any] = {"timestamp": time.time()}
|
|
584
554
|
|
|
585
555
|
if "services" in requested:
|
|
586
556
|
filtered_status["services"] = _get_services_status()
|
|
@@ -595,12 +565,11 @@ def _build_filtered_status(requested: set[str], context: t.Any) -> dict:
|
|
|
595
565
|
|
|
596
566
|
|
|
597
567
|
def _register_filtered_status_tool(mcp_app: t.Any) -> None:
|
|
598
|
-
@mcp_app.tool()
|
|
568
|
+
@mcp_app.tool() # type: ignore[misc]
|
|
599
569
|
async def get_filtered_status(components: str = "all") -> str:
|
|
600
570
|
client_id = "mcp_client"
|
|
601
571
|
|
|
602
572
|
try:
|
|
603
|
-
# Security validation with component filter data
|
|
604
573
|
await validate_status_request(
|
|
605
574
|
client_id, "get_filtered_status", {"components": components}
|
|
606
575
|
)
|
|
@@ -612,12 +581,10 @@ def _register_filtered_status_tool(mcp_app: t.Any) -> None:
|
|
|
612
581
|
|
|
613
582
|
|
|
614
583
|
async def _process_filtered_status_request(client_id: str, components: str) -> str:
|
|
615
|
-
"""Process filtered status request with component validation."""
|
|
616
584
|
requested, error = _validate_status_components(components)
|
|
617
585
|
if error:
|
|
618
586
|
return _format_status_error(error)
|
|
619
587
|
|
|
620
|
-
# Use secure status operation with timeout
|
|
621
588
|
async with await secure_status_operation(
|
|
622
589
|
client_id, "get_filtered_status", timeout=20.0
|
|
623
590
|
):
|
|
@@ -625,7 +592,6 @@ async def _process_filtered_status_request(client_id: str, components: str) -> s
|
|
|
625
592
|
|
|
626
593
|
|
|
627
594
|
async def _collect_status_data(client_id: str, requested: set[str]) -> str:
|
|
628
|
-
"""Collect status data based on requested components."""
|
|
629
595
|
context = get_context()
|
|
630
596
|
|
|
631
597
|
if "all" in requested:
|
|
@@ -636,7 +602,6 @@ async def _collect_status_data(client_id: str, requested: set[str]) -> str:
|
|
|
636
602
|
if not context:
|
|
637
603
|
return _format_status_error("Server context not available")
|
|
638
604
|
|
|
639
|
-
# Use bounded operation for filtered status
|
|
640
605
|
raw_status = await execute_bounded_status_operation(
|
|
641
606
|
"filtered_status",
|
|
642
607
|
client_id,
|
|
@@ -648,8 +613,7 @@ async def _collect_status_data(client_id: str, requested: set[str]) -> str:
|
|
|
648
613
|
return _apply_secure_formatting(raw_status, context)
|
|
649
614
|
|
|
650
615
|
|
|
651
|
-
def _apply_secure_formatting(raw_status: dict, context: t.Any) -> str:
|
|
652
|
-
"""Apply secure formatting to status data."""
|
|
616
|
+
def _apply_secure_formatting(raw_status: dict[str, t.Any], context: t.Any) -> str:
|
|
653
617
|
project_root = context.config.project_path if context else None
|
|
654
618
|
secure_status = format_secure_status(
|
|
655
619
|
raw_status,
|
|
@@ -660,6 +624,5 @@ def _apply_secure_formatting(raw_status: dict, context: t.Any) -> str:
|
|
|
660
624
|
|
|
661
625
|
|
|
662
626
|
def _format_status_error(error_message: str) -> str:
|
|
663
|
-
"""Format status error response consistently."""
|
|
664
627
|
formatter = get_secure_status_formatter()
|
|
665
628
|
return json.dumps(formatter.format_error_response(error_message), indent=2)
|