mcp-ticketer 0.3.1__py3-none-any.whl → 0.3.2__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 mcp-ticketer might be problematic. Click here for more details.
- mcp_ticketer/__version__.py +1 -1
- mcp_ticketer/adapters/aitrackdown.py +12 -15
- mcp_ticketer/adapters/github.py +7 -4
- mcp_ticketer/adapters/jira.py +23 -22
- mcp_ticketer/adapters/linear/__init__.py +1 -1
- mcp_ticketer/adapters/linear/adapter.py +88 -89
- mcp_ticketer/adapters/linear/client.py +71 -52
- mcp_ticketer/adapters/linear/mappers.py +88 -68
- mcp_ticketer/adapters/linear/queries.py +28 -7
- mcp_ticketer/adapters/linear/types.py +57 -50
- mcp_ticketer/adapters/linear.py +2 -2
- mcp_ticketer/cli/adapter_diagnostics.py +86 -51
- mcp_ticketer/cli/diagnostics.py +165 -72
- mcp_ticketer/cli/linear_commands.py +156 -113
- mcp_ticketer/cli/main.py +153 -82
- mcp_ticketer/cli/simple_health.py +73 -45
- mcp_ticketer/cli/utils.py +15 -10
- mcp_ticketer/core/config.py +23 -19
- mcp_ticketer/core/env_discovery.py +5 -4
- mcp_ticketer/core/env_loader.py +109 -86
- mcp_ticketer/core/exceptions.py +20 -18
- mcp_ticketer/core/models.py +9 -0
- mcp_ticketer/core/project_config.py +1 -1
- mcp_ticketer/mcp/server.py +294 -139
- mcp_ticketer/queue/health_monitor.py +152 -121
- mcp_ticketer/queue/manager.py +11 -4
- mcp_ticketer/queue/queue.py +15 -3
- mcp_ticketer/queue/run_worker.py +1 -1
- mcp_ticketer/queue/ticket_registry.py +190 -132
- mcp_ticketer/queue/worker.py +54 -25
- {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/METADATA +1 -1
- mcp_ticketer-0.3.2.dist-info/RECORD +59 -0
- mcp_ticketer-0.3.1.dist-info/RECORD +0 -59
- {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/licenses/LICENSE +0 -0
- {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/top_level.txt +0 -0
mcp_ticketer/cli/diagnostics.py
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
"""Comprehensive diagnostics and self-diagnosis functionality for MCP Ticketer."""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import json
|
|
5
4
|
import logging
|
|
6
|
-
import os
|
|
7
5
|
import sys
|
|
8
6
|
from datetime import datetime, timedelta
|
|
9
7
|
from pathlib import Path
|
|
10
|
-
from typing import Any, Dict, List, Optional
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
11
9
|
|
|
12
10
|
import typer
|
|
13
11
|
from rich.console import Console
|
|
14
|
-
from rich.panel import Panel
|
|
15
12
|
from rich.table import Table
|
|
16
|
-
|
|
13
|
+
|
|
17
14
|
|
|
18
15
|
def get_config():
|
|
19
16
|
"""Get configuration using the real configuration system."""
|
|
20
17
|
from ..core.config import ConfigurationManager
|
|
18
|
+
|
|
21
19
|
config_manager = ConfigurationManager()
|
|
22
20
|
return config_manager.load_config()
|
|
23
21
|
|
|
22
|
+
|
|
24
23
|
def safe_import_registry():
|
|
25
24
|
"""Safely import adapter registry with fallback."""
|
|
26
25
|
try:
|
|
27
26
|
from ..core.registry import AdapterRegistry
|
|
27
|
+
|
|
28
28
|
return AdapterRegistry
|
|
29
29
|
except ImportError:
|
|
30
|
+
|
|
30
31
|
class MockRegistry:
|
|
31
32
|
@staticmethod
|
|
32
33
|
def get_adapter(adapter_type):
|
|
@@ -34,6 +35,7 @@ def safe_import_registry():
|
|
|
34
35
|
|
|
35
36
|
return MockRegistry
|
|
36
37
|
|
|
38
|
+
|
|
37
39
|
def safe_import_queue_manager():
|
|
38
40
|
"""Safely import worker manager with fallback."""
|
|
39
41
|
try:
|
|
@@ -63,10 +65,15 @@ def safe_import_queue_manager():
|
|
|
63
65
|
return {"total": 0, "failed": 0, "pending": 0, "completed": 0}
|
|
64
66
|
|
|
65
67
|
def health_check(self):
|
|
66
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
"status": "degraded",
|
|
70
|
+
"score": 50,
|
|
71
|
+
"details": "Running in fallback mode",
|
|
72
|
+
}
|
|
67
73
|
|
|
68
74
|
return MockWorkerManager
|
|
69
75
|
|
|
76
|
+
|
|
70
77
|
# Initialize with safe imports
|
|
71
78
|
AdapterRegistry = safe_import_registry()
|
|
72
79
|
WorkerManager = safe_import_queue_manager()
|
|
@@ -125,6 +132,7 @@ class SystemDiagnostics:
|
|
|
125
132
|
"""Get current version information."""
|
|
126
133
|
try:
|
|
127
134
|
from ..__version__ import __version__
|
|
135
|
+
|
|
128
136
|
return __version__
|
|
129
137
|
except ImportError:
|
|
130
138
|
return "unknown"
|
|
@@ -135,7 +143,11 @@ class SystemDiagnostics:
|
|
|
135
143
|
"python_version": sys.version,
|
|
136
144
|
"platform": sys.platform,
|
|
137
145
|
"working_directory": str(Path.cwd()),
|
|
138
|
-
"config_path":
|
|
146
|
+
"config_path": (
|
|
147
|
+
str(self.config.config_file)
|
|
148
|
+
if hasattr(self.config, "config_file")
|
|
149
|
+
else "unknown"
|
|
150
|
+
),
|
|
139
151
|
}
|
|
140
152
|
|
|
141
153
|
async def _diagnose_configuration(self) -> Dict[str, Any]:
|
|
@@ -166,6 +178,7 @@ class SystemDiagnostics:
|
|
|
166
178
|
|
|
167
179
|
# Try to detect adapters from environment variables
|
|
168
180
|
import os
|
|
181
|
+
|
|
169
182
|
env_adapters = []
|
|
170
183
|
if os.getenv("LINEAR_API_KEY"):
|
|
171
184
|
env_adapters.append("linear")
|
|
@@ -178,15 +191,20 @@ class SystemDiagnostics:
|
|
|
178
191
|
config_status["default_adapter"] = "aitrackdown"
|
|
179
192
|
|
|
180
193
|
if env_adapters:
|
|
181
|
-
console.print(
|
|
194
|
+
console.print(
|
|
195
|
+
f"ℹ️ Detected {len(env_adapters)} adapter(s) from environment: {', '.join(env_adapters)}"
|
|
196
|
+
)
|
|
182
197
|
else:
|
|
183
|
-
console.print(
|
|
198
|
+
console.print(
|
|
199
|
+
"ℹ️ No adapter environment variables detected, using aitrackdown"
|
|
200
|
+
)
|
|
184
201
|
|
|
185
202
|
return config_status
|
|
186
203
|
|
|
187
204
|
try:
|
|
188
205
|
# Check adapter configurations using the same approach as working commands
|
|
189
206
|
from .utils import CommonPatterns
|
|
207
|
+
|
|
190
208
|
raw_config = CommonPatterns.load_config()
|
|
191
209
|
adapters_config = raw_config.get("adapters", {})
|
|
192
210
|
config_status["adapters_configured"] = len(adapters_config)
|
|
@@ -206,13 +224,15 @@ class SystemDiagnostics:
|
|
|
206
224
|
try:
|
|
207
225
|
# Use the same adapter creation approach as working commands
|
|
208
226
|
adapter = CommonPatterns.get_adapter(override_adapter=name)
|
|
209
|
-
|
|
227
|
+
|
|
210
228
|
# Test adapter validation if available
|
|
211
|
-
if hasattr(adapter,
|
|
229
|
+
if hasattr(adapter, "validate_credentials"):
|
|
212
230
|
is_valid, error = adapter.validate_credentials()
|
|
213
231
|
if is_valid:
|
|
214
232
|
console.print(f"✅ {name}: credentials valid")
|
|
215
|
-
self.successes.append(
|
|
233
|
+
self.successes.append(
|
|
234
|
+
f"{name} adapter configured correctly"
|
|
235
|
+
)
|
|
216
236
|
else:
|
|
217
237
|
issue = f"{name}: credential validation failed - {error}"
|
|
218
238
|
config_status["issues"].append(issue)
|
|
@@ -239,7 +259,7 @@ class SystemDiagnostics:
|
|
|
239
259
|
async def _diagnose_adapters(self) -> Dict[str, Any]:
|
|
240
260
|
"""Diagnose adapter functionality."""
|
|
241
261
|
console.print("\n🔌 [yellow]Adapter Diagnosis[/yellow]")
|
|
242
|
-
|
|
262
|
+
|
|
243
263
|
adapter_status = {
|
|
244
264
|
"total_adapters": 0,
|
|
245
265
|
"healthy_adapters": 0,
|
|
@@ -250,6 +270,7 @@ class SystemDiagnostics:
|
|
|
250
270
|
try:
|
|
251
271
|
# Use the same configuration loading approach as working commands
|
|
252
272
|
from .utils import CommonPatterns
|
|
273
|
+
|
|
253
274
|
raw_config = CommonPatterns.load_config()
|
|
254
275
|
adapters_config = raw_config.get("adapters", {})
|
|
255
276
|
adapter_status["total_adapters"] = len(adapters_config)
|
|
@@ -268,11 +289,12 @@ class SystemDiagnostics:
|
|
|
268
289
|
try:
|
|
269
290
|
# Use the same adapter creation approach as working commands
|
|
270
291
|
from .utils import CommonPatterns
|
|
292
|
+
|
|
271
293
|
adapter = CommonPatterns.get_adapter(override_adapter=adapter_type)
|
|
272
|
-
|
|
294
|
+
|
|
273
295
|
# Test basic adapter functionality
|
|
274
296
|
test_start = datetime.now()
|
|
275
|
-
|
|
297
|
+
|
|
276
298
|
# Try to list tickets (non-destructive test)
|
|
277
299
|
try:
|
|
278
300
|
await adapter.list(limit=1)
|
|
@@ -317,7 +339,11 @@ class SystemDiagnostics:
|
|
|
317
339
|
"failure_rate": 0.0,
|
|
318
340
|
"health_score": 0,
|
|
319
341
|
"worker_start_test": {"attempted": False, "success": False, "error": None},
|
|
320
|
-
"queue_operation_test": {
|
|
342
|
+
"queue_operation_test": {
|
|
343
|
+
"attempted": False,
|
|
344
|
+
"success": False,
|
|
345
|
+
"error": None,
|
|
346
|
+
},
|
|
321
347
|
}
|
|
322
348
|
|
|
323
349
|
try:
|
|
@@ -339,7 +365,9 @@ class SystemDiagnostics:
|
|
|
339
365
|
queue_status["worker_pid"] = worker_status.get("pid")
|
|
340
366
|
|
|
341
367
|
if queue_status["worker_running"]:
|
|
342
|
-
console.print(
|
|
368
|
+
console.print(
|
|
369
|
+
f"✅ Queue worker running (PID: {queue_status['worker_pid']})"
|
|
370
|
+
)
|
|
343
371
|
self.successes.append("Queue worker is running")
|
|
344
372
|
else:
|
|
345
373
|
console.print("⚠️ Queue worker not running - attempting to start...")
|
|
@@ -353,8 +381,12 @@ class SystemDiagnostics:
|
|
|
353
381
|
queue_status["worker_running"] = True
|
|
354
382
|
self.successes.append("Queue worker started successfully")
|
|
355
383
|
else:
|
|
356
|
-
console.print(
|
|
357
|
-
|
|
384
|
+
console.print(
|
|
385
|
+
f"❌ Failed to start queue worker: {start_test['error']}"
|
|
386
|
+
)
|
|
387
|
+
self.issues.append(
|
|
388
|
+
f"Queue worker startup failed: {start_test['error']}"
|
|
389
|
+
)
|
|
358
390
|
|
|
359
391
|
# Test 3: Get queue statistics
|
|
360
392
|
console.print("🔍 Analyzing queue statistics...")
|
|
@@ -377,7 +409,9 @@ class SystemDiagnostics:
|
|
|
377
409
|
self.warnings.append(warning)
|
|
378
410
|
console.print(f"⚠️ {warning}")
|
|
379
411
|
else:
|
|
380
|
-
console.print(
|
|
412
|
+
console.print(
|
|
413
|
+
f"✅ Queue failure rate: {failure_rate:.1f}% ({failed_items}/{total_items})"
|
|
414
|
+
)
|
|
381
415
|
|
|
382
416
|
# Test 4: Test actual queue operations
|
|
383
417
|
console.print("🔍 Testing queue operations...")
|
|
@@ -388,21 +422,30 @@ class SystemDiagnostics:
|
|
|
388
422
|
console.print("✅ Queue operations test passed")
|
|
389
423
|
self.successes.append("Queue operations working correctly")
|
|
390
424
|
else:
|
|
391
|
-
console.print(
|
|
392
|
-
|
|
425
|
+
console.print(
|
|
426
|
+
f"❌ Queue operations test failed: {operation_test['error']}"
|
|
427
|
+
)
|
|
428
|
+
self.issues.append(
|
|
429
|
+
f"Queue operations failed: {operation_test['error']}"
|
|
430
|
+
)
|
|
393
431
|
|
|
394
432
|
# Calculate health score based on actual tests
|
|
395
433
|
health_score = 100
|
|
396
434
|
if not queue_status["worker_running"]:
|
|
397
435
|
health_score -= 30
|
|
398
|
-
if
|
|
436
|
+
if (
|
|
437
|
+
not queue_status["worker_start_test"]["success"]
|
|
438
|
+
and queue_status["worker_start_test"]["attempted"]
|
|
439
|
+
):
|
|
399
440
|
health_score -= 20
|
|
400
441
|
if not queue_status["queue_operation_test"]["success"]:
|
|
401
442
|
health_score -= 30
|
|
402
443
|
health_score -= min(queue_status["failure_rate"], 20)
|
|
403
444
|
queue_status["health_score"] = max(0, health_score)
|
|
404
445
|
|
|
405
|
-
console.print(
|
|
446
|
+
console.print(
|
|
447
|
+
f"📊 Queue health score: {queue_status['health_score']}/100 (based on active testing)"
|
|
448
|
+
)
|
|
406
449
|
|
|
407
450
|
except Exception as e:
|
|
408
451
|
issue = f"Queue system diagnosis failed: {str(e)}"
|
|
@@ -417,23 +460,28 @@ class SystemDiagnostics:
|
|
|
417
460
|
"attempted": True,
|
|
418
461
|
"success": False,
|
|
419
462
|
"error": None,
|
|
420
|
-
"details": None
|
|
463
|
+
"details": None,
|
|
421
464
|
}
|
|
422
465
|
|
|
423
466
|
try:
|
|
424
467
|
# Try to start worker using the worker manager
|
|
425
|
-
if hasattr(self.worker_manager,
|
|
468
|
+
if hasattr(self.worker_manager, "start"):
|
|
426
469
|
result = self.worker_manager.start()
|
|
427
470
|
test_result["success"] = result
|
|
428
|
-
test_result["details"] =
|
|
471
|
+
test_result["details"] = (
|
|
472
|
+
"Worker started successfully"
|
|
473
|
+
if result
|
|
474
|
+
else "Worker failed to start"
|
|
475
|
+
)
|
|
429
476
|
else:
|
|
430
477
|
# Try alternative method - use CLI command
|
|
431
478
|
import subprocess
|
|
479
|
+
|
|
432
480
|
result = subprocess.run(
|
|
433
481
|
["mcp-ticketer", "queue", "worker", "start"],
|
|
434
482
|
capture_output=True,
|
|
435
483
|
text=True,
|
|
436
|
-
timeout=10
|
|
484
|
+
timeout=10,
|
|
437
485
|
)
|
|
438
486
|
if result.returncode == 0:
|
|
439
487
|
test_result["success"] = True
|
|
@@ -454,18 +502,18 @@ class SystemDiagnostics:
|
|
|
454
502
|
"attempted": True,
|
|
455
503
|
"success": False,
|
|
456
504
|
"error": None,
|
|
457
|
-
"details": None
|
|
505
|
+
"details": None,
|
|
458
506
|
}
|
|
459
507
|
|
|
460
508
|
try:
|
|
461
509
|
# Test creating a simple queue item (diagnostic test)
|
|
462
|
-
from ..core.models import
|
|
510
|
+
from ..core.models import Priority, Task
|
|
463
511
|
from ..queue.queue import Queue
|
|
464
512
|
|
|
465
513
|
test_task = Task(
|
|
466
514
|
title="[DIAGNOSTIC TEST] Queue functionality test",
|
|
467
515
|
description="This is a diagnostic test - safe to ignore",
|
|
468
|
-
priority=Priority.LOW
|
|
516
|
+
priority=Priority.LOW,
|
|
469
517
|
)
|
|
470
518
|
|
|
471
519
|
# Try to queue the test task using the correct Queue.add() method
|
|
@@ -473,7 +521,7 @@ class SystemDiagnostics:
|
|
|
473
521
|
queue_id = queue.add(
|
|
474
522
|
ticket_data=test_task.model_dump(),
|
|
475
523
|
adapter="aitrackdown",
|
|
476
|
-
operation="create"
|
|
524
|
+
operation="create",
|
|
477
525
|
)
|
|
478
526
|
test_result["success"] = True
|
|
479
527
|
test_result["details"] = f"Test task queued successfully: {queue_id}"
|
|
@@ -489,25 +537,25 @@ class SystemDiagnostics:
|
|
|
489
537
|
"attempted": True,
|
|
490
538
|
"success": False,
|
|
491
539
|
"error": None,
|
|
492
|
-
"details": None
|
|
540
|
+
"details": None,
|
|
493
541
|
}
|
|
494
542
|
|
|
495
543
|
try:
|
|
496
544
|
# Test if we can at least create a task directly (bypass queue)
|
|
497
|
-
from ..core.models import Task, Priority
|
|
498
545
|
from ..adapters.aitrackdown import AITrackdownAdapter
|
|
546
|
+
from ..core.models import Priority, Task
|
|
499
547
|
|
|
500
548
|
test_task = Task(
|
|
501
549
|
title="[DIAGNOSTIC TEST] Direct adapter test",
|
|
502
550
|
description="Testing direct adapter functionality",
|
|
503
|
-
priority=Priority.LOW
|
|
551
|
+
priority=Priority.LOW,
|
|
504
552
|
)
|
|
505
553
|
|
|
506
554
|
# Try direct adapter creation
|
|
507
555
|
adapter_config = {
|
|
508
556
|
"type": "aitrackdown",
|
|
509
557
|
"enabled": True,
|
|
510
|
-
"base_path": "/tmp/mcp-ticketer-diagnostic-test"
|
|
558
|
+
"base_path": "/tmp/mcp-ticketer-diagnostic-test",
|
|
511
559
|
}
|
|
512
560
|
|
|
513
561
|
adapter = AITrackdownAdapter(adapter_config)
|
|
@@ -527,7 +575,7 @@ class SystemDiagnostics:
|
|
|
527
575
|
async def _analyze_recent_logs(self) -> Dict[str, Any]:
|
|
528
576
|
"""Analyze recent log entries for issues."""
|
|
529
577
|
console.print("\n📝 [yellow]Recent Log Analysis[/yellow]")
|
|
530
|
-
|
|
578
|
+
|
|
531
579
|
log_analysis = {
|
|
532
580
|
"log_files_found": [],
|
|
533
581
|
"recent_errors": [],
|
|
@@ -551,7 +599,9 @@ class SystemDiagnostics:
|
|
|
551
599
|
if not log_analysis["log_files_found"]:
|
|
552
600
|
console.print("ℹ️ No log files found in standard locations")
|
|
553
601
|
else:
|
|
554
|
-
console.print(
|
|
602
|
+
console.print(
|
|
603
|
+
f"✅ Found logs in {len(log_analysis['log_files_found'])} location(s)"
|
|
604
|
+
)
|
|
555
605
|
|
|
556
606
|
except Exception as e:
|
|
557
607
|
issue = f"Log analysis failed: {str(e)}"
|
|
@@ -560,11 +610,16 @@ class SystemDiagnostics:
|
|
|
560
610
|
|
|
561
611
|
return log_analysis
|
|
562
612
|
|
|
563
|
-
async def _analyze_log_directory(
|
|
613
|
+
async def _analyze_log_directory(
|
|
614
|
+
self, log_path: Path, log_analysis: Dict[str, Any]
|
|
615
|
+
):
|
|
564
616
|
"""Analyze logs in a specific directory."""
|
|
565
617
|
try:
|
|
566
618
|
for log_file in log_path.glob("*.log"):
|
|
567
|
-
if
|
|
619
|
+
if (
|
|
620
|
+
log_file.stat().st_mtime
|
|
621
|
+
> (datetime.now() - timedelta(hours=24)).timestamp()
|
|
622
|
+
):
|
|
568
623
|
await self._parse_log_file(log_file, log_analysis)
|
|
569
624
|
except Exception as e:
|
|
570
625
|
self.warnings.append(f"Could not analyze logs in {log_path}: {str(e)}")
|
|
@@ -572,9 +627,9 @@ class SystemDiagnostics:
|
|
|
572
627
|
async def _parse_log_file(self, log_file: Path, log_analysis: Dict[str, Any]):
|
|
573
628
|
"""Parse individual log file for issues."""
|
|
574
629
|
try:
|
|
575
|
-
with open(log_file
|
|
630
|
+
with open(log_file) as f:
|
|
576
631
|
lines = f.readlines()[-100:] # Last 100 lines
|
|
577
|
-
|
|
632
|
+
|
|
578
633
|
for line in lines:
|
|
579
634
|
if "ERROR" in line:
|
|
580
635
|
log_analysis["recent_errors"].append(line.strip())
|
|
@@ -587,7 +642,7 @@ class SystemDiagnostics:
|
|
|
587
642
|
async def _analyze_performance(self) -> Dict[str, Any]:
|
|
588
643
|
"""Analyze system performance metrics."""
|
|
589
644
|
console.print("\n⚡ [yellow]Performance Analysis[/yellow]")
|
|
590
|
-
|
|
645
|
+
|
|
591
646
|
performance = {
|
|
592
647
|
"response_times": {},
|
|
593
648
|
"throughput": {},
|
|
@@ -597,16 +652,16 @@ class SystemDiagnostics:
|
|
|
597
652
|
try:
|
|
598
653
|
# Test basic operations performance
|
|
599
654
|
start_time = datetime.now()
|
|
600
|
-
|
|
655
|
+
|
|
601
656
|
# Test configuration loading
|
|
602
657
|
config_start = datetime.now()
|
|
603
658
|
_ = get_config()
|
|
604
659
|
config_time = (datetime.now() - config_start).total_seconds()
|
|
605
660
|
performance["response_times"]["config_load"] = config_time
|
|
606
|
-
|
|
661
|
+
|
|
607
662
|
if config_time > 1.0:
|
|
608
663
|
self.warnings.append(f"Slow configuration loading: {config_time:.2f}s")
|
|
609
|
-
|
|
664
|
+
|
|
610
665
|
console.print(f"📊 Configuration load time: {config_time:.3f}s")
|
|
611
666
|
|
|
612
667
|
except Exception as e:
|
|
@@ -621,23 +676,33 @@ class SystemDiagnostics:
|
|
|
621
676
|
recommendations = []
|
|
622
677
|
|
|
623
678
|
if self.issues:
|
|
624
|
-
recommendations.append(
|
|
625
|
-
|
|
679
|
+
recommendations.append(
|
|
680
|
+
"🚨 Critical issues detected - immediate attention required"
|
|
681
|
+
)
|
|
682
|
+
|
|
626
683
|
if any("Queue worker not running" in issue for issue in self.issues):
|
|
627
|
-
recommendations.append(
|
|
628
|
-
|
|
684
|
+
recommendations.append(
|
|
685
|
+
"• Restart queue worker: mcp-ticketer queue worker restart"
|
|
686
|
+
)
|
|
687
|
+
|
|
629
688
|
if any("failure rate" in issue.lower() for issue in self.issues):
|
|
630
689
|
recommendations.append("• Check queue system logs for error patterns")
|
|
631
|
-
recommendations.append(
|
|
632
|
-
|
|
690
|
+
recommendations.append(
|
|
691
|
+
"• Consider clearing failed queue items: mcp-ticketer queue clear --failed"
|
|
692
|
+
)
|
|
693
|
+
|
|
633
694
|
if any("No adapters configured" in issue for issue in self.issues):
|
|
634
|
-
recommendations.append(
|
|
695
|
+
recommendations.append(
|
|
696
|
+
"• Configure at least one adapter: mcp-ticketer init-aitrackdown"
|
|
697
|
+
)
|
|
635
698
|
|
|
636
699
|
if self.warnings:
|
|
637
700
|
recommendations.append("⚠️ Warnings detected - monitoring recommended")
|
|
638
701
|
|
|
639
702
|
if not self.issues and not self.warnings:
|
|
640
|
-
recommendations.append(
|
|
703
|
+
recommendations.append(
|
|
704
|
+
"✅ System appears healthy - no immediate action required"
|
|
705
|
+
)
|
|
641
706
|
|
|
642
707
|
return recommendations
|
|
643
708
|
|
|
@@ -661,7 +726,9 @@ class SystemDiagnostics:
|
|
|
661
726
|
status_text = "HEALTHY"
|
|
662
727
|
status_icon = "✅"
|
|
663
728
|
|
|
664
|
-
console.print(
|
|
729
|
+
console.print(
|
|
730
|
+
f"\n{status_icon} [bold {status_color}]System Status: {status_text}[/bold {status_color}]"
|
|
731
|
+
)
|
|
665
732
|
|
|
666
733
|
# Statistics
|
|
667
734
|
stats_table = Table(show_header=True, header_style="bold blue")
|
|
@@ -670,36 +737,62 @@ class SystemDiagnostics:
|
|
|
670
737
|
stats_table.add_column("Details")
|
|
671
738
|
|
|
672
739
|
# Add component statuses
|
|
673
|
-
config_status =
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
stats_table.add_row(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
740
|
+
config_status = (
|
|
741
|
+
"✅ OK"
|
|
742
|
+
if not any("configuration" in issue.lower() for issue in self.issues)
|
|
743
|
+
else "❌ FAILED"
|
|
744
|
+
)
|
|
745
|
+
stats_table.add_row(
|
|
746
|
+
"Configuration",
|
|
747
|
+
config_status,
|
|
748
|
+
f"{report['configuration']['adapters_configured']} adapters",
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
queue_health = report["queue_system"]["health_score"]
|
|
752
|
+
queue_status = (
|
|
753
|
+
"✅ OK"
|
|
754
|
+
if queue_health > 80
|
|
755
|
+
else "⚠️ DEGRADED" if queue_health > 50 else "❌ FAILED"
|
|
756
|
+
)
|
|
757
|
+
stats_table.add_row(
|
|
758
|
+
"Queue System", queue_status, f"{queue_health}/100 health score"
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
adapter_stats = report["adapters"]
|
|
762
|
+
adapter_status = (
|
|
763
|
+
"✅ OK" if adapter_stats["failed_adapters"] == 0 else "❌ FAILED"
|
|
764
|
+
)
|
|
765
|
+
stats_table.add_row(
|
|
766
|
+
"Adapters",
|
|
767
|
+
adapter_status,
|
|
768
|
+
f"{adapter_stats['healthy_adapters']}/{adapter_stats['total_adapters']} healthy",
|
|
769
|
+
)
|
|
683
770
|
|
|
684
771
|
console.print(stats_table)
|
|
685
772
|
|
|
686
773
|
# Issues and recommendations
|
|
687
774
|
if self.issues:
|
|
688
|
-
console.print(
|
|
775
|
+
console.print(
|
|
776
|
+
f"\n🚨 [bold red]Critical Issues ({len(self.issues)}):[/bold red]"
|
|
777
|
+
)
|
|
689
778
|
for issue in self.issues:
|
|
690
779
|
console.print(f" • {issue}")
|
|
691
780
|
|
|
692
781
|
if self.warnings:
|
|
693
|
-
console.print(
|
|
782
|
+
console.print(
|
|
783
|
+
f"\n⚠️ [bold yellow]Warnings ({len(self.warnings)}):[/bold yellow]"
|
|
784
|
+
)
|
|
694
785
|
for warning in self.warnings:
|
|
695
786
|
console.print(f" • {warning}")
|
|
696
787
|
|
|
697
|
-
if report[
|
|
698
|
-
console.print(
|
|
699
|
-
for rec in report[
|
|
788
|
+
if report["recommendations"]:
|
|
789
|
+
console.print("\n💡 [bold blue]Recommendations:[/bold blue]")
|
|
790
|
+
for rec in report["recommendations"]:
|
|
700
791
|
console.print(f" {rec}")
|
|
701
792
|
|
|
702
|
-
console.print(
|
|
793
|
+
console.print(
|
|
794
|
+
f"\n📊 [bold]Summary:[/bold] {len(self.successes)} successes, {len(self.warnings)} warnings, {len(self.issues)} critical issues"
|
|
795
|
+
)
|
|
703
796
|
|
|
704
797
|
|
|
705
798
|
async def run_diagnostics(
|
|
@@ -711,7 +804,7 @@ async def run_diagnostics(
|
|
|
711
804
|
report = await diagnostics.run_full_diagnosis()
|
|
712
805
|
|
|
713
806
|
if output_file:
|
|
714
|
-
with open(output_file,
|
|
807
|
+
with open(output_file, "w") as f:
|
|
715
808
|
json.dump(report, f, indent=2)
|
|
716
809
|
console.print(f"\n📄 Full report saved to: {output_file}")
|
|
717
810
|
|