crackerjack 0.29.0__py3-none-any.whl → 0.31.4__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.

Files changed (158) hide show
  1. crackerjack/CLAUDE.md +1005 -0
  2. crackerjack/RULES.md +380 -0
  3. crackerjack/__init__.py +42 -13
  4. crackerjack/__main__.py +225 -253
  5. crackerjack/agents/__init__.py +41 -0
  6. crackerjack/agents/architect_agent.py +281 -0
  7. crackerjack/agents/base.py +169 -0
  8. crackerjack/agents/coordinator.py +512 -0
  9. crackerjack/agents/documentation_agent.py +498 -0
  10. crackerjack/agents/dry_agent.py +388 -0
  11. crackerjack/agents/formatting_agent.py +245 -0
  12. crackerjack/agents/import_optimization_agent.py +281 -0
  13. crackerjack/agents/performance_agent.py +669 -0
  14. crackerjack/agents/proactive_agent.py +104 -0
  15. crackerjack/agents/refactoring_agent.py +788 -0
  16. crackerjack/agents/security_agent.py +529 -0
  17. crackerjack/agents/test_creation_agent.py +652 -0
  18. crackerjack/agents/test_specialist_agent.py +486 -0
  19. crackerjack/agents/tracker.py +212 -0
  20. crackerjack/api.py +560 -0
  21. crackerjack/cli/__init__.py +24 -0
  22. crackerjack/cli/facade.py +104 -0
  23. crackerjack/cli/handlers.py +267 -0
  24. crackerjack/cli/interactive.py +471 -0
  25. crackerjack/cli/options.py +401 -0
  26. crackerjack/cli/utils.py +18 -0
  27. crackerjack/code_cleaner.py +670 -0
  28. crackerjack/config/__init__.py +19 -0
  29. crackerjack/config/hooks.py +218 -0
  30. crackerjack/core/__init__.py +0 -0
  31. crackerjack/core/async_workflow_orchestrator.py +406 -0
  32. crackerjack/core/autofix_coordinator.py +200 -0
  33. crackerjack/core/container.py +104 -0
  34. crackerjack/core/enhanced_container.py +542 -0
  35. crackerjack/core/performance.py +243 -0
  36. crackerjack/core/phase_coordinator.py +561 -0
  37. crackerjack/core/proactive_workflow.py +316 -0
  38. crackerjack/core/session_coordinator.py +289 -0
  39. crackerjack/core/workflow_orchestrator.py +640 -0
  40. crackerjack/dynamic_config.py +577 -0
  41. crackerjack/errors.py +263 -41
  42. crackerjack/executors/__init__.py +11 -0
  43. crackerjack/executors/async_hook_executor.py +431 -0
  44. crackerjack/executors/cached_hook_executor.py +242 -0
  45. crackerjack/executors/hook_executor.py +345 -0
  46. crackerjack/executors/individual_hook_executor.py +669 -0
  47. crackerjack/intelligence/__init__.py +44 -0
  48. crackerjack/intelligence/adaptive_learning.py +751 -0
  49. crackerjack/intelligence/agent_orchestrator.py +551 -0
  50. crackerjack/intelligence/agent_registry.py +414 -0
  51. crackerjack/intelligence/agent_selector.py +502 -0
  52. crackerjack/intelligence/integration.py +290 -0
  53. crackerjack/interactive.py +576 -315
  54. crackerjack/managers/__init__.py +11 -0
  55. crackerjack/managers/async_hook_manager.py +135 -0
  56. crackerjack/managers/hook_manager.py +137 -0
  57. crackerjack/managers/publish_manager.py +411 -0
  58. crackerjack/managers/test_command_builder.py +151 -0
  59. crackerjack/managers/test_executor.py +435 -0
  60. crackerjack/managers/test_manager.py +258 -0
  61. crackerjack/managers/test_manager_backup.py +1124 -0
  62. crackerjack/managers/test_progress.py +144 -0
  63. crackerjack/mcp/__init__.py +0 -0
  64. crackerjack/mcp/cache.py +336 -0
  65. crackerjack/mcp/client_runner.py +104 -0
  66. crackerjack/mcp/context.py +615 -0
  67. crackerjack/mcp/dashboard.py +636 -0
  68. crackerjack/mcp/enhanced_progress_monitor.py +479 -0
  69. crackerjack/mcp/file_monitor.py +336 -0
  70. crackerjack/mcp/progress_components.py +569 -0
  71. crackerjack/mcp/progress_monitor.py +949 -0
  72. crackerjack/mcp/rate_limiter.py +332 -0
  73. crackerjack/mcp/server.py +22 -0
  74. crackerjack/mcp/server_core.py +244 -0
  75. crackerjack/mcp/service_watchdog.py +501 -0
  76. crackerjack/mcp/state.py +395 -0
  77. crackerjack/mcp/task_manager.py +257 -0
  78. crackerjack/mcp/tools/__init__.py +17 -0
  79. crackerjack/mcp/tools/core_tools.py +249 -0
  80. crackerjack/mcp/tools/error_analyzer.py +308 -0
  81. crackerjack/mcp/tools/execution_tools.py +370 -0
  82. crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
  83. crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
  84. crackerjack/mcp/tools/intelligence_tools.py +314 -0
  85. crackerjack/mcp/tools/monitoring_tools.py +502 -0
  86. crackerjack/mcp/tools/proactive_tools.py +384 -0
  87. crackerjack/mcp/tools/progress_tools.py +141 -0
  88. crackerjack/mcp/tools/utility_tools.py +341 -0
  89. crackerjack/mcp/tools/workflow_executor.py +360 -0
  90. crackerjack/mcp/websocket/__init__.py +14 -0
  91. crackerjack/mcp/websocket/app.py +39 -0
  92. crackerjack/mcp/websocket/endpoints.py +559 -0
  93. crackerjack/mcp/websocket/jobs.py +253 -0
  94. crackerjack/mcp/websocket/server.py +116 -0
  95. crackerjack/mcp/websocket/websocket_handler.py +78 -0
  96. crackerjack/mcp/websocket_server.py +10 -0
  97. crackerjack/models/__init__.py +31 -0
  98. crackerjack/models/config.py +93 -0
  99. crackerjack/models/config_adapter.py +230 -0
  100. crackerjack/models/protocols.py +118 -0
  101. crackerjack/models/task.py +154 -0
  102. crackerjack/monitoring/ai_agent_watchdog.py +450 -0
  103. crackerjack/monitoring/regression_prevention.py +638 -0
  104. crackerjack/orchestration/__init__.py +0 -0
  105. crackerjack/orchestration/advanced_orchestrator.py +970 -0
  106. crackerjack/orchestration/execution_strategies.py +341 -0
  107. crackerjack/orchestration/test_progress_streamer.py +636 -0
  108. crackerjack/plugins/__init__.py +15 -0
  109. crackerjack/plugins/base.py +200 -0
  110. crackerjack/plugins/hooks.py +246 -0
  111. crackerjack/plugins/loader.py +335 -0
  112. crackerjack/plugins/managers.py +259 -0
  113. crackerjack/py313.py +8 -3
  114. crackerjack/services/__init__.py +22 -0
  115. crackerjack/services/cache.py +314 -0
  116. crackerjack/services/config.py +347 -0
  117. crackerjack/services/config_integrity.py +99 -0
  118. crackerjack/services/contextual_ai_assistant.py +516 -0
  119. crackerjack/services/coverage_ratchet.py +347 -0
  120. crackerjack/services/debug.py +736 -0
  121. crackerjack/services/dependency_monitor.py +617 -0
  122. crackerjack/services/enhanced_filesystem.py +439 -0
  123. crackerjack/services/file_hasher.py +151 -0
  124. crackerjack/services/filesystem.py +395 -0
  125. crackerjack/services/git.py +165 -0
  126. crackerjack/services/health_metrics.py +611 -0
  127. crackerjack/services/initialization.py +847 -0
  128. crackerjack/services/log_manager.py +286 -0
  129. crackerjack/services/logging.py +174 -0
  130. crackerjack/services/metrics.py +578 -0
  131. crackerjack/services/pattern_cache.py +362 -0
  132. crackerjack/services/pattern_detector.py +515 -0
  133. crackerjack/services/performance_benchmarks.py +653 -0
  134. crackerjack/services/security.py +163 -0
  135. crackerjack/services/server_manager.py +234 -0
  136. crackerjack/services/smart_scheduling.py +144 -0
  137. crackerjack/services/tool_version_service.py +61 -0
  138. crackerjack/services/unified_config.py +437 -0
  139. crackerjack/services/version_checker.py +248 -0
  140. crackerjack/slash_commands/__init__.py +14 -0
  141. crackerjack/slash_commands/init.md +122 -0
  142. crackerjack/slash_commands/run.md +163 -0
  143. crackerjack/slash_commands/status.md +127 -0
  144. crackerjack-0.31.4.dist-info/METADATA +742 -0
  145. crackerjack-0.31.4.dist-info/RECORD +148 -0
  146. crackerjack-0.31.4.dist-info/entry_points.txt +2 -0
  147. crackerjack/.gitignore +0 -34
  148. crackerjack/.libcst.codemod.yaml +0 -18
  149. crackerjack/.pdm.toml +0 -1
  150. crackerjack/.pre-commit-config-ai.yaml +0 -149
  151. crackerjack/.pre-commit-config-fast.yaml +0 -69
  152. crackerjack/.pre-commit-config.yaml +0 -114
  153. crackerjack/crackerjack.py +0 -4140
  154. crackerjack/pyproject.toml +0 -285
  155. crackerjack-0.29.0.dist-info/METADATA +0 -1289
  156. crackerjack-0.29.0.dist-info/RECORD +0 -17
  157. {crackerjack-0.29.0.dist-info → crackerjack-0.31.4.dist-info}/WHEEL +0 -0
  158. {crackerjack-0.29.0.dist-info → crackerjack-0.31.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,502 @@
1
+ import json
2
+ import time
3
+ import typing as t
4
+ from contextlib import suppress
5
+
6
+ from crackerjack.mcp.context import get_context
7
+
8
+
9
+ def _suggest_agent_for_context(state_manager) -> dict[str, t.Any]:
10
+ """Suggest appropriate agents based on current development context."""
11
+ suggestions = {
12
+ "recommended_agent": None,
13
+ "reason": "",
14
+ "usage": "",
15
+ "priority": "MEDIUM",
16
+ }
17
+
18
+ # Check for errors or failures that need specific agents
19
+ with suppress(Exception):
20
+ recent_errors = getattr(state_manager, "recent_errors", [])
21
+ stage_statuses = _get_stage_status_dict(state_manager)
22
+
23
+ # Test failures suggest test specialist
24
+ if stage_statuses.get("tests") == "failed" or any(
25
+ "test" in str(error).lower() for error in recent_errors
26
+ ):
27
+ suggestions.update(
28
+ {
29
+ "recommended_agent": "crackerjack-test-specialist",
30
+ "reason": "Test failures detected - specialist needed for debugging and fixes",
31
+ "usage": 'Task tool with subagent_type="crackerjack-test-specialist"',
32
+ "priority": "HIGH",
33
+ }
34
+ )
35
+ # Security issues suggest security auditor
36
+ elif any(
37
+ "security" in str(error).lower() or "bandit" in str(error).lower()
38
+ for error in recent_errors
39
+ ):
40
+ suggestions.update(
41
+ {
42
+ "recommended_agent": "security-auditor",
43
+ "reason": "Security issues detected - immediate audit required",
44
+ "usage": 'Task tool with subagent_type="security-auditor"',
45
+ "priority": "HIGH",
46
+ }
47
+ )
48
+ # Complexity issues suggest architect
49
+ elif any("complex" in str(error).lower() for error in recent_errors):
50
+ suggestions.update(
51
+ {
52
+ "recommended_agent": "crackerjack-architect",
53
+ "reason": "Complexity issues detected - architectural review needed",
54
+ "usage": 'Task tool with subagent_type="crackerjack-architect"',
55
+ "priority": "HIGH",
56
+ }
57
+ )
58
+ # Default recommendation for Python projects
59
+ else:
60
+ suggestions.update(
61
+ {
62
+ "recommended_agent": "crackerjack-architect",
63
+ "reason": "Python project - ensure crackerjack compliance from the start",
64
+ "usage": 'Task tool with subagent_type="crackerjack-architect"',
65
+ "priority": "MEDIUM",
66
+ }
67
+ )
68
+
69
+ return suggestions
70
+
71
+
72
+ def _create_error_response(message: str, success: bool = False) -> str:
73
+ """Utility function to create standardized error responses."""
74
+ import json
75
+
76
+ return json.dumps({"error": message, "success": success})
77
+
78
+
79
+ def _get_stage_status_dict(state_manager) -> dict[str, str]:
80
+ stages = ["fast", "comprehensive", "tests", "cleaning"]
81
+ return {stage: state_manager.get_stage_status(stage) for stage in stages}
82
+
83
+
84
+ def _get_session_info(state_manager) -> dict[str, t.Any]:
85
+ return {
86
+ "total_iterations": getattr(state_manager, "iteration_count", 0),
87
+ "current_iteration": getattr(state_manager, "current_iteration", 0),
88
+ "session_active": getattr(state_manager, "session_active", False),
89
+ }
90
+
91
+
92
+ def _determine_next_action(state_manager) -> dict[str, t.Any]:
93
+ stage_priorities = [
94
+ ("fast", "Fast hooks not completed"),
95
+ ("tests", "Tests not completed"),
96
+ ("comprehensive", "Comprehensive hooks not completed"),
97
+ ]
98
+
99
+ for stage, reason in stage_priorities:
100
+ if state_manager.get_stage_status(stage) != "completed":
101
+ return {
102
+ "recommended_action": "run_stage",
103
+ "parameters": {"stage": stage},
104
+ "reason": reason,
105
+ }
106
+
107
+ return {
108
+ "recommended_action": "complete",
109
+ "parameters": {},
110
+ "reason": "All stages completed successfully",
111
+ }
112
+
113
+
114
+ def _build_server_stats(context) -> dict[str, t.Any]:
115
+ return {
116
+ "server_info": {
117
+ "project_path": str(context.config.project_path),
118
+ "websocket_port": getattr(context, "websocket_server_port", None),
119
+ "websocket_active": getattr(context, "websocket_server_process", None)
120
+ is not None,
121
+ },
122
+ "rate_limiting": {
123
+ "enabled": context.rate_limiter is not None,
124
+ "config": context.rate_limiter.config.__dict__
125
+ if context.rate_limiter
126
+ else None,
127
+ },
128
+ "resource_usage": {
129
+ "temp_files_count": len(list(context.progress_dir.glob("*.json")))
130
+ if context.progress_dir.exists()
131
+ else 0,
132
+ "progress_dir": str(context.progress_dir),
133
+ },
134
+ "timestamp": time.time(),
135
+ }
136
+
137
+
138
+ def _add_state_manager_stats(stats: dict, state_manager) -> None:
139
+ if state_manager:
140
+ stats["state_manager"] = {
141
+ "iteration_count": getattr(state_manager, "iteration_count", 0),
142
+ "session_active": getattr(state_manager, "session_active", False),
143
+ "issues_count": len(getattr(state_manager, "issues", [])),
144
+ }
145
+
146
+
147
+ def _get_active_jobs(context) -> list[dict[str, t.Any]]:
148
+ """Get information about active jobs from progress files."""
149
+ jobs = []
150
+ if not context.progress_dir.exists():
151
+ return jobs
152
+
153
+ with suppress(Exception):
154
+ for progress_file in context.progress_dir.glob("job-*.json"):
155
+ try:
156
+ progress_data = json.loads(progress_file.read_text())
157
+ jobs.append(
158
+ {
159
+ "job_id": progress_data.get("job_id", "unknown"),
160
+ "status": progress_data.get("status", "unknown"),
161
+ "iteration": progress_data.get("iteration", 0),
162
+ "max_iterations": progress_data.get("max_iterations", 10),
163
+ "current_stage": progress_data.get("current_stage", "unknown"),
164
+ "overall_progress": progress_data.get("overall_progress", 0),
165
+ "stage_progress": progress_data.get("stage_progress", 0),
166
+ "message": progress_data.get("message", ""),
167
+ "timestamp": progress_data.get("timestamp", ""),
168
+ "error_counts": progress_data.get("error_counts", {}),
169
+ },
170
+ )
171
+ except (json.JSONDecodeError, KeyError):
172
+ continue
173
+
174
+ return jobs
175
+
176
+
177
+ async def _get_comprehensive_status() -> dict[str, t.Any]:
178
+ """Get comprehensive status of MCP server, WebSocket server, and active jobs."""
179
+ try:
180
+ context = get_context()
181
+ except RuntimeError:
182
+ context = None
183
+
184
+ if not context:
185
+ return {"error": "Server context not available"}
186
+
187
+ try:
188
+ # Get server status
189
+ from crackerjack.services.server_manager import (
190
+ find_mcp_server_processes,
191
+ find_websocket_server_processes,
192
+ )
193
+
194
+ mcp_processes = find_mcp_server_processes()
195
+ websocket_processes = find_websocket_server_processes()
196
+
197
+ # Get active jobs
198
+ active_jobs = _get_active_jobs(context)
199
+
200
+ # Get WebSocket server status from context
201
+ websocket_status = None
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}"}
208
+
209
+ # Build comprehensive status
210
+ status = {
211
+ "services": {
212
+ "mcp_server": {
213
+ "running": len(mcp_processes) > 0,
214
+ "processes": mcp_processes,
215
+ },
216
+ "websocket_server": {
217
+ "running": len(websocket_processes) > 0,
218
+ "processes": websocket_processes,
219
+ "port": getattr(context, "websocket_server_port", 8675),
220
+ "status": websocket_status,
221
+ },
222
+ },
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
+ }
238
+
239
+ # Add state manager stats
240
+ state_manager = getattr(context, "state_manager", None)
241
+ _add_state_manager_stats(status["server_stats"], state_manager)
242
+
243
+ # Add agent suggestions based on current context
244
+ if state_manager:
245
+ status["agent_suggestions"] = _suggest_agent_for_context(state_manager)
246
+
247
+ return status
248
+
249
+ except Exception as e:
250
+ return {"error": f"Failed to get comprehensive status: {e}"}
251
+
252
+
253
+ def register_monitoring_tools(mcp_app: t.Any) -> None:
254
+ _register_stage_status_tool(mcp_app)
255
+ _register_next_action_tool(mcp_app)
256
+ _register_server_stats_tool(mcp_app)
257
+ _register_comprehensive_status_tool(mcp_app)
258
+ _register_command_help_tool(mcp_app)
259
+ _register_filtered_status_tool(mcp_app)
260
+
261
+
262
+ def _register_stage_status_tool(mcp_app: t.Any) -> None:
263
+ @mcp_app.tool()
264
+ async def get_stage_status() -> str:
265
+ context = get_context()
266
+ if not context:
267
+ return _create_error_response("Server context not available")
268
+
269
+ try:
270
+ state_manager = getattr(context, "state_manager", None)
271
+ if not state_manager:
272
+ return _create_error_response("State manager not available")
273
+
274
+ result = {
275
+ "stages": _get_stage_status_dict(state_manager),
276
+ "session": _get_session_info(state_manager),
277
+ "timestamp": time.time(),
278
+ }
279
+
280
+ return json.dumps(result, indent=2)
281
+
282
+ except Exception as e:
283
+ return f'{{"error": "Failed to get stage status: {e}"}}'
284
+
285
+
286
+ def _register_next_action_tool(mcp_app: t.Any) -> None:
287
+ @mcp_app.tool()
288
+ async def get_next_action() -> str:
289
+ context = get_context()
290
+ if not context:
291
+ return _create_error_response("Server context not available")
292
+
293
+ try:
294
+ state_manager = getattr(context, "state_manager", None)
295
+ if not state_manager:
296
+ return '{"recommended_action": "initialize", "reason": "No state manager available"}'
297
+
298
+ action = _determine_next_action(state_manager)
299
+ return json.dumps(action, indent=2)
300
+
301
+ except Exception as e:
302
+ return f'{{"error": "Failed to determine next action: {e}"}}'
303
+
304
+
305
+ def _register_server_stats_tool(mcp_app: t.Any) -> None:
306
+ @mcp_app.tool()
307
+ async def get_server_stats() -> str:
308
+ context = get_context()
309
+ if not context:
310
+ return _create_error_response("Server context not available")
311
+
312
+ try:
313
+ stats = _build_server_stats(context)
314
+ state_manager = getattr(context, "state_manager", None)
315
+ _add_state_manager_stats(stats, state_manager)
316
+
317
+ return json.dumps(stats, indent=2)
318
+
319
+ except Exception as e:
320
+ return f'{{"error": "Failed to get server stats: {e}"}}'
321
+
322
+
323
+ def _register_comprehensive_status_tool(mcp_app: t.Any) -> None:
324
+ @mcp_app.tool()
325
+ async def get_comprehensive_status() -> str:
326
+ """Get comprehensive status of MCP server, WebSocket server, and active jobs.
327
+
328
+ This is the main status tool used by the /crackerjack:status slash command.
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
+ """
337
+ try:
338
+ status = await _get_comprehensive_status()
339
+ return json.dumps(status, indent=2)
340
+ except Exception as e:
341
+ import traceback
342
+
343
+ return f'{{"error": "Failed to get comprehensive status: {e}", "traceback": "{traceback.format_exc()}"}}'
344
+
345
+
346
+ def _register_command_help_tool(mcp_app: t.Any) -> None:
347
+ @mcp_app.tool()
348
+ async def list_slash_commands() -> str:
349
+ """List all available crackerjack slash commands with descriptions and usage."""
350
+ try:
351
+ commands = {
352
+ "/crackerjack:run": {
353
+ "description": "Run full iterative auto-fixing with AI agent, tests, progress tracking, and verbose output",
354
+ "usage": "Direct execution - no parameters needed",
355
+ "features": [
356
+ "Up to 10 iterations of quality improvement",
357
+ "Real-time WebSocket progress streaming",
358
+ "Advanced orchestrator with adaptive strategies",
359
+ "Automatic service management",
360
+ "Debug mode support",
361
+ ],
362
+ },
363
+ "/crackerjack:status": {
364
+ "description": "Get comprehensive system status including servers, jobs, and resource usage",
365
+ "usage": "Direct execution - no parameters needed",
366
+ "features": [
367
+ "MCP server health monitoring",
368
+ "WebSocket server status",
369
+ "Active job tracking",
370
+ "Resource usage metrics",
371
+ "Error counts and progress data",
372
+ ],
373
+ },
374
+ "/crackerjack:init": {
375
+ "description": "Initialize or update crackerjack configuration with smart configuration merging",
376
+ "usage": "Optional parameters: target_path (defaults to current directory)",
377
+ "kwargs": {
378
+ "force": "boolean - force overwrite existing configurations"
379
+ },
380
+ "features": [
381
+ "Smart merge preserving existing configurations",
382
+ "Universal Python project compatibility",
383
+ "pyproject.toml and pre-commit setup",
384
+ "Documentation and MCP configuration",
385
+ "Non-destructive configuration updates",
386
+ ],
387
+ },
388
+ }
389
+
390
+ return json.dumps(
391
+ {
392
+ "available_commands": list(commands.keys()),
393
+ "command_details": commands,
394
+ "total_commands": len(commands),
395
+ },
396
+ indent=2,
397
+ )
398
+
399
+ except Exception as e:
400
+ return json.dumps(
401
+ {"error": f"Failed to list slash commands: {e}", "success": False},
402
+ indent=2,
403
+ )
404
+
405
+
406
+ def _validate_status_components(components: str) -> tuple[set[str], str | None]:
407
+ """Validate and parse status components."""
408
+ valid_components = {"services", "jobs", "resources", "all"}
409
+ requested = {c.strip().lower() for c in components.split(",")}
410
+
411
+ invalid = requested - valid_components
412
+ if invalid:
413
+ return set(), f"Invalid components: {invalid}. Valid: {valid_components}"
414
+
415
+ return requested, None
416
+
417
+
418
+ def _get_services_status() -> dict:
419
+ """Get services status information."""
420
+ from crackerjack.services.server_manager import (
421
+ find_mcp_server_processes,
422
+ find_websocket_server_processes,
423
+ )
424
+
425
+ mcp_processes = find_mcp_server_processes()
426
+ websocket_processes = find_websocket_server_processes()
427
+
428
+ return {
429
+ "mcp_server": {
430
+ "running": len(mcp_processes) > 0,
431
+ "processes": mcp_processes,
432
+ },
433
+ "websocket_server": {
434
+ "running": len(websocket_processes) > 0,
435
+ "processes": websocket_processes,
436
+ },
437
+ }
438
+
439
+
440
+ def _get_resources_status(context: t.Any) -> dict:
441
+ """Get resources status information."""
442
+ temp_files_count = (
443
+ len(list(context.progress_dir.glob("*.json")))
444
+ if context.progress_dir.exists()
445
+ else 0
446
+ )
447
+
448
+ return {
449
+ "temp_files_count": temp_files_count,
450
+ "progress_dir": str(context.progress_dir),
451
+ }
452
+
453
+
454
+ def _build_filtered_status(requested: set[str], context: t.Any) -> dict:
455
+ """Build filtered status based on requested components."""
456
+ filtered_status = {"timestamp": time.time()}
457
+
458
+ if "services" in requested:
459
+ filtered_status["services"] = _get_services_status()
460
+
461
+ if "jobs" in requested:
462
+ filtered_status["jobs"] = {"active": _get_active_jobs(context)}
463
+
464
+ if "resources" in requested:
465
+ filtered_status["resources"] = _get_resources_status(context)
466
+
467
+ return filtered_status
468
+
469
+
470
+ def _register_filtered_status_tool(mcp_app: t.Any) -> None:
471
+ @mcp_app.tool()
472
+ async def get_filtered_status(components: str = "all") -> str:
473
+ """Get specific status components for better performance.
474
+
475
+ Args:
476
+ components: Comma-separated list of components to include:
477
+ 'services', 'jobs', 'resources', 'all' (default)
478
+ """
479
+ try:
480
+ requested, error = _validate_status_components(components)
481
+ if error:
482
+ return json.dumps({"error": error, "success": False}, indent=2)
483
+
484
+ # If 'all' is requested, get everything
485
+ if "all" in requested:
486
+ status = await _get_comprehensive_status()
487
+ return json.dumps(status, indent=2)
488
+
489
+ context = get_context()
490
+ if not context:
491
+ return json.dumps(
492
+ {"error": "Server context not available", "success": False}
493
+ )
494
+
495
+ filtered_status = _build_filtered_status(requested, context)
496
+ return json.dumps(filtered_status, indent=2)
497
+
498
+ except Exception as e:
499
+ return json.dumps(
500
+ {"error": f"Failed to get filtered status: {e}", "success": False},
501
+ indent=2,
502
+ )