mcp-ticketer 0.3.1__py3-none-any.whl → 0.3.3__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.

Files changed (41) hide show
  1. mcp_ticketer/__version__.py +1 -1
  2. mcp_ticketer/adapters/aitrackdown.py +164 -36
  3. mcp_ticketer/adapters/github.py +11 -8
  4. mcp_ticketer/adapters/jira.py +29 -28
  5. mcp_ticketer/adapters/linear/__init__.py +1 -1
  6. mcp_ticketer/adapters/linear/adapter.py +105 -104
  7. mcp_ticketer/adapters/linear/client.py +78 -59
  8. mcp_ticketer/adapters/linear/mappers.py +93 -73
  9. mcp_ticketer/adapters/linear/queries.py +28 -7
  10. mcp_ticketer/adapters/linear/types.py +67 -60
  11. mcp_ticketer/adapters/linear.py +2 -2
  12. mcp_ticketer/cli/adapter_diagnostics.py +87 -52
  13. mcp_ticketer/cli/codex_configure.py +6 -6
  14. mcp_ticketer/cli/diagnostics.py +180 -88
  15. mcp_ticketer/cli/linear_commands.py +156 -113
  16. mcp_ticketer/cli/main.py +153 -82
  17. mcp_ticketer/cli/simple_health.py +74 -51
  18. mcp_ticketer/cli/utils.py +15 -10
  19. mcp_ticketer/core/config.py +23 -19
  20. mcp_ticketer/core/env_discovery.py +5 -4
  21. mcp_ticketer/core/env_loader.py +114 -91
  22. mcp_ticketer/core/exceptions.py +22 -20
  23. mcp_ticketer/core/models.py +9 -0
  24. mcp_ticketer/core/project_config.py +1 -1
  25. mcp_ticketer/mcp/constants.py +58 -0
  26. mcp_ticketer/mcp/dto.py +195 -0
  27. mcp_ticketer/mcp/response_builder.py +206 -0
  28. mcp_ticketer/mcp/server.py +361 -1182
  29. mcp_ticketer/queue/health_monitor.py +166 -135
  30. mcp_ticketer/queue/manager.py +70 -19
  31. mcp_ticketer/queue/queue.py +24 -5
  32. mcp_ticketer/queue/run_worker.py +1 -1
  33. mcp_ticketer/queue/ticket_registry.py +203 -145
  34. mcp_ticketer/queue/worker.py +79 -43
  35. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.3.dist-info}/METADATA +1 -1
  36. mcp_ticketer-0.3.3.dist-info/RECORD +62 -0
  37. mcp_ticketer-0.3.1.dist-info/RECORD +0 -59
  38. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.3.dist-info}/WHEEL +0 -0
  39. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.3.dist-info}/entry_points.txt +0 -0
  40. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.3.dist-info}/licenses/LICENSE +0 -0
  41. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.3.dist-info}/top_level.txt +0 -0
@@ -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, Tuple
8
+ from typing import Any, 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
- from rich.text import Text
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 {"status": "degraded", "score": 50, "details": "Running in fallback mode"}
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()
@@ -101,7 +108,7 @@ class SystemDiagnostics:
101
108
  self.queue_available = False
102
109
  console.print(f"⚠️ Could not initialize worker manager: {e}")
103
110
 
104
- async def run_full_diagnosis(self) -> Dict[str, Any]:
111
+ async def run_full_diagnosis(self) -> dict[str, Any]:
105
112
  """Run complete system diagnosis and return detailed report."""
106
113
  console.print("\n🔍 [bold blue]MCP Ticketer System Diagnosis[/bold blue]")
107
114
  console.print("=" * 60)
@@ -125,20 +132,25 @@ 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"
131
139
 
132
- def _get_system_info(self) -> Dict[str, Any]:
140
+ def _get_system_info(self) -> dict[str, Any]:
133
141
  """Gather system information."""
134
142
  return {
135
143
  "python_version": sys.version,
136
144
  "platform": sys.platform,
137
145
  "working_directory": str(Path.cwd()),
138
- "config_path": str(self.config.config_file) if hasattr(self.config, 'config_file') else "unknown",
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
- async def _diagnose_configuration(self) -> Dict[str, Any]:
153
+ async def _diagnose_configuration(self) -> dict[str, Any]:
142
154
  """Diagnose configuration issues."""
143
155
  console.print("\n📋 [yellow]Configuration Analysis[/yellow]")
144
156
 
@@ -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(f"ℹ️ Detected {len(env_adapters)} adapter(s) from environment: {', '.join(env_adapters)}")
194
+ console.print(
195
+ f"ℹ️ Detected {len(env_adapters)} adapter(s) from environment: {', '.join(env_adapters)}"
196
+ )
182
197
  else:
183
- console.print("ℹ️ No adapter environment variables detected, using aitrackdown")
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)
@@ -202,17 +220,19 @@ class SystemDiagnostics:
202
220
  console.print(f"✅ {len(adapters_config)} adapter(s) configured")
203
221
 
204
222
  # Check each adapter configuration
205
- for name, adapter_config in adapters_config.items():
223
+ for name, _adapter_config in adapters_config.items():
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, 'validate_credentials'):
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(f"{name} adapter configured correctly")
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)
@@ -236,10 +256,10 @@ class SystemDiagnostics:
236
256
 
237
257
  return config_status
238
258
 
239
- async def _diagnose_adapters(self) -> Dict[str, Any]:
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,13 +270,13 @@ 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)
256
277
 
257
278
  for name, adapter_config in adapters_config.items():
258
279
  adapter_type = adapter_config.get("type", name)
259
- config_dict = adapter_config
260
280
 
261
281
  details = {
262
282
  "type": adapter_type,
@@ -268,11 +288,12 @@ class SystemDiagnostics:
268
288
  try:
269
289
  # Use the same adapter creation approach as working commands
270
290
  from .utils import CommonPatterns
291
+
271
292
  adapter = CommonPatterns.get_adapter(override_adapter=adapter_type)
272
-
293
+
273
294
  # Test basic adapter functionality
274
295
  test_start = datetime.now()
275
-
296
+
276
297
  # Try to list tickets (non-destructive test)
277
298
  try:
278
299
  await adapter.list(limit=1)
@@ -305,7 +326,7 @@ class SystemDiagnostics:
305
326
 
306
327
  return adapter_status
307
328
 
308
- async def _diagnose_queue_system(self) -> Dict[str, Any]:
329
+ async def _diagnose_queue_system(self) -> dict[str, Any]:
309
330
  """Diagnose queue system health with active testing."""
310
331
  console.print("\n⚡ [yellow]Queue System Diagnosis[/yellow]")
311
332
 
@@ -317,7 +338,11 @@ class SystemDiagnostics:
317
338
  "failure_rate": 0.0,
318
339
  "health_score": 0,
319
340
  "worker_start_test": {"attempted": False, "success": False, "error": None},
320
- "queue_operation_test": {"attempted": False, "success": False, "error": None},
341
+ "queue_operation_test": {
342
+ "attempted": False,
343
+ "success": False,
344
+ "error": None,
345
+ },
321
346
  }
322
347
 
323
348
  try:
@@ -339,7 +364,9 @@ class SystemDiagnostics:
339
364
  queue_status["worker_pid"] = worker_status.get("pid")
340
365
 
341
366
  if queue_status["worker_running"]:
342
- console.print(f"✅ Queue worker running (PID: {queue_status['worker_pid']})")
367
+ console.print(
368
+ f"✅ Queue worker running (PID: {queue_status['worker_pid']})"
369
+ )
343
370
  self.successes.append("Queue worker is running")
344
371
  else:
345
372
  console.print("⚠️ Queue worker not running - attempting to start...")
@@ -353,8 +380,12 @@ class SystemDiagnostics:
353
380
  queue_status["worker_running"] = True
354
381
  self.successes.append("Queue worker started successfully")
355
382
  else:
356
- console.print(f"❌ Failed to start queue worker: {start_test['error']}")
357
- self.issues.append(f"Queue worker startup failed: {start_test['error']}")
383
+ console.print(
384
+ f" Failed to start queue worker: {start_test['error']}"
385
+ )
386
+ self.issues.append(
387
+ f"Queue worker startup failed: {start_test['error']}"
388
+ )
358
389
 
359
390
  # Test 3: Get queue statistics
360
391
  console.print("🔍 Analyzing queue statistics...")
@@ -377,7 +408,9 @@ class SystemDiagnostics:
377
408
  self.warnings.append(warning)
378
409
  console.print(f"⚠️ {warning}")
379
410
  else:
380
- console.print(f"✅ Queue failure rate: {failure_rate:.1f}% ({failed_items}/{total_items})")
411
+ console.print(
412
+ f"✅ Queue failure rate: {failure_rate:.1f}% ({failed_items}/{total_items})"
413
+ )
381
414
 
382
415
  # Test 4: Test actual queue operations
383
416
  console.print("🔍 Testing queue operations...")
@@ -388,21 +421,30 @@ class SystemDiagnostics:
388
421
  console.print("✅ Queue operations test passed")
389
422
  self.successes.append("Queue operations working correctly")
390
423
  else:
391
- console.print(f"❌ Queue operations test failed: {operation_test['error']}")
392
- self.issues.append(f"Queue operations failed: {operation_test['error']}")
424
+ console.print(
425
+ f"Queue operations test failed: {operation_test['error']}"
426
+ )
427
+ self.issues.append(
428
+ f"Queue operations failed: {operation_test['error']}"
429
+ )
393
430
 
394
431
  # Calculate health score based on actual tests
395
432
  health_score = 100
396
433
  if not queue_status["worker_running"]:
397
434
  health_score -= 30
398
- if not queue_status["worker_start_test"]["success"] and queue_status["worker_start_test"]["attempted"]:
435
+ if (
436
+ not queue_status["worker_start_test"]["success"]
437
+ and queue_status["worker_start_test"]["attempted"]
438
+ ):
399
439
  health_score -= 20
400
440
  if not queue_status["queue_operation_test"]["success"]:
401
441
  health_score -= 30
402
442
  health_score -= min(queue_status["failure_rate"], 20)
403
443
  queue_status["health_score"] = max(0, health_score)
404
444
 
405
- console.print(f"📊 Queue health score: {queue_status['health_score']}/100 (based on active testing)")
445
+ console.print(
446
+ f"📊 Queue health score: {queue_status['health_score']}/100 (based on active testing)"
447
+ )
406
448
 
407
449
  except Exception as e:
408
450
  issue = f"Queue system diagnosis failed: {str(e)}"
@@ -411,29 +453,34 @@ class SystemDiagnostics:
411
453
 
412
454
  return queue_status
413
455
 
414
- async def _test_worker_startup(self) -> Dict[str, Any]:
456
+ async def _test_worker_startup(self) -> dict[str, Any]:
415
457
  """Test starting a queue worker."""
416
458
  test_result = {
417
459
  "attempted": True,
418
460
  "success": False,
419
461
  "error": None,
420
- "details": None
462
+ "details": None,
421
463
  }
422
464
 
423
465
  try:
424
466
  # Try to start worker using the worker manager
425
- if hasattr(self.worker_manager, 'start'):
467
+ if hasattr(self.worker_manager, "start"):
426
468
  result = self.worker_manager.start()
427
469
  test_result["success"] = result
428
- test_result["details"] = "Worker started successfully" if result else "Worker failed to start"
470
+ test_result["details"] = (
471
+ "Worker started successfully"
472
+ if result
473
+ else "Worker failed to start"
474
+ )
429
475
  else:
430
476
  # Try alternative method - use CLI command
431
477
  import subprocess
478
+
432
479
  result = subprocess.run(
433
480
  ["mcp-ticketer", "queue", "worker", "start"],
434
481
  capture_output=True,
435
482
  text=True,
436
- timeout=10
483
+ timeout=10,
437
484
  )
438
485
  if result.returncode == 0:
439
486
  test_result["success"] = True
@@ -448,24 +495,24 @@ class SystemDiagnostics:
448
495
 
449
496
  return test_result
450
497
 
451
- async def _test_queue_operations(self) -> Dict[str, Any]:
498
+ async def _test_queue_operations(self) -> dict[str, Any]:
452
499
  """Test basic queue operations."""
453
500
  test_result = {
454
501
  "attempted": True,
455
502
  "success": False,
456
503
  "error": None,
457
- "details": None
504
+ "details": None,
458
505
  }
459
506
 
460
507
  try:
461
508
  # Test creating a simple queue item (diagnostic test)
462
- from ..core.models import Task, Priority
509
+ from ..core.models import Priority, Task
463
510
  from ..queue.queue import Queue
464
511
 
465
512
  test_task = Task(
466
513
  title="[DIAGNOSTIC TEST] Queue functionality test",
467
514
  description="This is a diagnostic test - safe to ignore",
468
- priority=Priority.LOW
515
+ priority=Priority.LOW,
469
516
  )
470
517
 
471
518
  # Try to queue the test task using the correct Queue.add() method
@@ -473,7 +520,7 @@ class SystemDiagnostics:
473
520
  queue_id = queue.add(
474
521
  ticket_data=test_task.model_dump(),
475
522
  adapter="aitrackdown",
476
- operation="create"
523
+ operation="create",
477
524
  )
478
525
  test_result["success"] = True
479
526
  test_result["details"] = f"Test task queued successfully: {queue_id}"
@@ -483,31 +530,31 @@ class SystemDiagnostics:
483
530
 
484
531
  return test_result
485
532
 
486
- async def _test_basic_queue_functionality(self) -> Dict[str, Any]:
533
+ async def _test_basic_queue_functionality(self) -> dict[str, Any]:
487
534
  """Test basic queue functionality in fallback mode."""
488
535
  test_result = {
489
536
  "attempted": True,
490
537
  "success": False,
491
538
  "error": None,
492
- "details": None
539
+ "details": None,
493
540
  }
494
541
 
495
542
  try:
496
543
  # Test if we can at least create a task directly (bypass queue)
497
- from ..core.models import Task, Priority
498
544
  from ..adapters.aitrackdown import AITrackdownAdapter
545
+ from ..core.models import Priority, Task
499
546
 
500
547
  test_task = Task(
501
548
  title="[DIAGNOSTIC TEST] Direct adapter test",
502
549
  description="Testing direct adapter functionality",
503
- priority=Priority.LOW
550
+ priority=Priority.LOW,
504
551
  )
505
552
 
506
553
  # Try direct adapter creation
507
554
  adapter_config = {
508
555
  "type": "aitrackdown",
509
556
  "enabled": True,
510
- "base_path": "/tmp/mcp-ticketer-diagnostic-test"
557
+ "base_path": "/tmp/mcp-ticketer-diagnostic-test",
511
558
  }
512
559
 
513
560
  adapter = AITrackdownAdapter(adapter_config)
@@ -524,10 +571,10 @@ class SystemDiagnostics:
524
571
 
525
572
  return test_result
526
573
 
527
- async def _analyze_recent_logs(self) -> Dict[str, Any]:
574
+ async def _analyze_recent_logs(self) -> dict[str, Any]:
528
575
  """Analyze recent log entries for issues."""
529
576
  console.print("\n📝 [yellow]Recent Log Analysis[/yellow]")
530
-
577
+
531
578
  log_analysis = {
532
579
  "log_files_found": [],
533
580
  "recent_errors": [],
@@ -551,7 +598,9 @@ class SystemDiagnostics:
551
598
  if not log_analysis["log_files_found"]:
552
599
  console.print("ℹ️ No log files found in standard locations")
553
600
  else:
554
- console.print(f"✅ Found logs in {len(log_analysis['log_files_found'])} location(s)")
601
+ console.print(
602
+ f"✅ Found logs in {len(log_analysis['log_files_found'])} location(s)"
603
+ )
555
604
 
556
605
  except Exception as e:
557
606
  issue = f"Log analysis failed: {str(e)}"
@@ -560,21 +609,26 @@ class SystemDiagnostics:
560
609
 
561
610
  return log_analysis
562
611
 
563
- async def _analyze_log_directory(self, log_path: Path, log_analysis: Dict[str, Any]):
612
+ async def _analyze_log_directory(
613
+ self, log_path: Path, log_analysis: dict[str, Any]
614
+ ):
564
615
  """Analyze logs in a specific directory."""
565
616
  try:
566
617
  for log_file in log_path.glob("*.log"):
567
- if log_file.stat().st_mtime > (datetime.now() - timedelta(hours=24)).timestamp():
618
+ if (
619
+ log_file.stat().st_mtime
620
+ > (datetime.now() - timedelta(hours=24)).timestamp()
621
+ ):
568
622
  await self._parse_log_file(log_file, log_analysis)
569
623
  except Exception as e:
570
624
  self.warnings.append(f"Could not analyze logs in {log_path}: {str(e)}")
571
625
 
572
- async def _parse_log_file(self, log_file: Path, log_analysis: Dict[str, Any]):
626
+ async def _parse_log_file(self, log_file: Path, log_analysis: dict[str, Any]):
573
627
  """Parse individual log file for issues."""
574
628
  try:
575
- with open(log_file, 'r') as f:
629
+ with open(log_file) as f:
576
630
  lines = f.readlines()[-100:] # Last 100 lines
577
-
631
+
578
632
  for line in lines:
579
633
  if "ERROR" in line:
580
634
  log_analysis["recent_errors"].append(line.strip())
@@ -584,10 +638,10 @@ class SystemDiagnostics:
584
638
  except Exception as e:
585
639
  self.warnings.append(f"Could not parse {log_file}: {str(e)}")
586
640
 
587
- async def _analyze_performance(self) -> Dict[str, Any]:
641
+ async def _analyze_performance(self) -> dict[str, Any]:
588
642
  """Analyze system performance metrics."""
589
643
  console.print("\n⚡ [yellow]Performance Analysis[/yellow]")
590
-
644
+
591
645
  performance = {
592
646
  "response_times": {},
593
647
  "throughput": {},
@@ -596,17 +650,17 @@ class SystemDiagnostics:
596
650
 
597
651
  try:
598
652
  # Test basic operations performance
599
- start_time = datetime.now()
600
-
653
+ datetime.now()
654
+
601
655
  # Test configuration loading
602
656
  config_start = datetime.now()
603
657
  _ = get_config()
604
658
  config_time = (datetime.now() - config_start).total_seconds()
605
659
  performance["response_times"]["config_load"] = config_time
606
-
660
+
607
661
  if config_time > 1.0:
608
662
  self.warnings.append(f"Slow configuration loading: {config_time:.2f}s")
609
-
663
+
610
664
  console.print(f"📊 Configuration load time: {config_time:.3f}s")
611
665
 
612
666
  except Exception as e:
@@ -616,32 +670,42 @@ class SystemDiagnostics:
616
670
 
617
671
  return performance
618
672
 
619
- def _generate_recommendations(self) -> List[str]:
673
+ def _generate_recommendations(self) -> list[str]:
620
674
  """Generate actionable recommendations based on diagnosis."""
621
675
  recommendations = []
622
676
 
623
677
  if self.issues:
624
- recommendations.append("🚨 Critical issues detected - immediate attention required")
625
-
678
+ recommendations.append(
679
+ "🚨 Critical issues detected - immediate attention required"
680
+ )
681
+
626
682
  if any("Queue worker not running" in issue for issue in self.issues):
627
- recommendations.append("• Restart queue worker: mcp-ticketer queue worker restart")
628
-
683
+ recommendations.append(
684
+ "• Restart queue worker: mcp-ticketer queue worker restart"
685
+ )
686
+
629
687
  if any("failure rate" in issue.lower() for issue in self.issues):
630
688
  recommendations.append("• Check queue system logs for error patterns")
631
- recommendations.append("• Consider clearing failed queue items: mcp-ticketer queue clear --failed")
632
-
689
+ recommendations.append(
690
+ "• Consider clearing failed queue items: mcp-ticketer queue clear --failed"
691
+ )
692
+
633
693
  if any("No adapters configured" in issue for issue in self.issues):
634
- recommendations.append("• Configure at least one adapter: mcp-ticketer init-aitrackdown")
694
+ recommendations.append(
695
+ "• Configure at least one adapter: mcp-ticketer init-aitrackdown"
696
+ )
635
697
 
636
698
  if self.warnings:
637
699
  recommendations.append("⚠️ Warnings detected - monitoring recommended")
638
700
 
639
701
  if not self.issues and not self.warnings:
640
- recommendations.append("✅ System appears healthy - no immediate action required")
702
+ recommendations.append(
703
+ "✅ System appears healthy - no immediate action required"
704
+ )
641
705
 
642
706
  return recommendations
643
707
 
644
- def _display_diagnosis_summary(self, report: Dict[str, Any]):
708
+ def _display_diagnosis_summary(self, report: dict[str, Any]):
645
709
  """Display a comprehensive diagnosis summary."""
646
710
  console.print("\n" + "=" * 60)
647
711
  console.print("📋 [bold green]DIAGNOSIS SUMMARY[/bold green]")
@@ -661,7 +725,9 @@ class SystemDiagnostics:
661
725
  status_text = "HEALTHY"
662
726
  status_icon = "✅"
663
727
 
664
- console.print(f"\n{status_icon} [bold {status_color}]System Status: {status_text}[/bold {status_color}]")
728
+ console.print(
729
+ f"\n{status_icon} [bold {status_color}]System Status: {status_text}[/bold {status_color}]"
730
+ )
665
731
 
666
732
  # Statistics
667
733
  stats_table = Table(show_header=True, header_style="bold blue")
@@ -670,36 +736,62 @@ class SystemDiagnostics:
670
736
  stats_table.add_column("Details")
671
737
 
672
738
  # Add component statuses
673
- config_status = "✅ OK" if not any("configuration" in issue.lower() for issue in self.issues) else "❌ FAILED"
674
- stats_table.add_row("Configuration", config_status, f"{report['configuration']['adapters_configured']} adapters")
675
-
676
- queue_health = report['queue_system']['health_score']
677
- queue_status = "✅ OK" if queue_health > 80 else "⚠️ DEGRADED" if queue_health > 50 else "❌ FAILED"
678
- stats_table.add_row("Queue System", queue_status, f"{queue_health}/100 health score")
679
-
680
- adapter_stats = report['adapters']
681
- adapter_status = "✅ OK" if adapter_stats['failed_adapters'] == 0 else "❌ FAILED"
682
- stats_table.add_row("Adapters", adapter_status, f"{adapter_stats['healthy_adapters']}/{adapter_stats['total_adapters']} healthy")
739
+ config_status = (
740
+ " OK"
741
+ if not any("configuration" in issue.lower() for issue in self.issues)
742
+ else "❌ FAILED"
743
+ )
744
+ stats_table.add_row(
745
+ "Configuration",
746
+ config_status,
747
+ f"{report['configuration']['adapters_configured']} adapters",
748
+ )
749
+
750
+ queue_health = report["queue_system"]["health_score"]
751
+ queue_status = (
752
+ "✅ OK"
753
+ if queue_health > 80
754
+ else "⚠️ DEGRADED" if queue_health > 50 else "❌ FAILED"
755
+ )
756
+ stats_table.add_row(
757
+ "Queue System", queue_status, f"{queue_health}/100 health score"
758
+ )
759
+
760
+ adapter_stats = report["adapters"]
761
+ adapter_status = (
762
+ "✅ OK" if adapter_stats["failed_adapters"] == 0 else "❌ FAILED"
763
+ )
764
+ stats_table.add_row(
765
+ "Adapters",
766
+ adapter_status,
767
+ f"{adapter_stats['healthy_adapters']}/{adapter_stats['total_adapters']} healthy",
768
+ )
683
769
 
684
770
  console.print(stats_table)
685
771
 
686
772
  # Issues and recommendations
687
773
  if self.issues:
688
- console.print(f"\n🚨 [bold red]Critical Issues ({len(self.issues)}):[/bold red]")
774
+ console.print(
775
+ f"\n🚨 [bold red]Critical Issues ({len(self.issues)}):[/bold red]"
776
+ )
689
777
  for issue in self.issues:
690
778
  console.print(f" • {issue}")
691
779
 
692
780
  if self.warnings:
693
- console.print(f"\n⚠️ [bold yellow]Warnings ({len(self.warnings)}):[/bold yellow]")
781
+ console.print(
782
+ f"\n⚠️ [bold yellow]Warnings ({len(self.warnings)}):[/bold yellow]"
783
+ )
694
784
  for warning in self.warnings:
695
785
  console.print(f" • {warning}")
696
786
 
697
- if report['recommendations']:
698
- console.print(f"\n💡 [bold blue]Recommendations:[/bold blue]")
699
- for rec in report['recommendations']:
787
+ if report["recommendations"]:
788
+ console.print("\n💡 [bold blue]Recommendations:[/bold blue]")
789
+ for rec in report["recommendations"]:
700
790
  console.print(f" {rec}")
701
791
 
702
- console.print(f"\n📊 [bold]Summary:[/bold] {len(self.successes)} successes, {len(self.warnings)} warnings, {len(self.issues)} critical issues")
792
+ console.print(
793
+ f"\n📊 [bold]Summary:[/bold] {len(self.successes)} successes, {len(self.warnings)} warnings, {len(self.issues)} critical issues"
794
+ )
703
795
 
704
796
 
705
797
  async def run_diagnostics(
@@ -711,7 +803,7 @@ async def run_diagnostics(
711
803
  report = await diagnostics.run_full_diagnosis()
712
804
 
713
805
  if output_file:
714
- with open(output_file, 'w') as f:
806
+ with open(output_file, "w") as f:
715
807
  json.dump(report, f, indent=2)
716
808
  console.print(f"\n📄 Full report saved to: {output_file}")
717
809