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.

Files changed (37) hide show
  1. mcp_ticketer/__version__.py +1 -1
  2. mcp_ticketer/adapters/aitrackdown.py +12 -15
  3. mcp_ticketer/adapters/github.py +7 -4
  4. mcp_ticketer/adapters/jira.py +23 -22
  5. mcp_ticketer/adapters/linear/__init__.py +1 -1
  6. mcp_ticketer/adapters/linear/adapter.py +88 -89
  7. mcp_ticketer/adapters/linear/client.py +71 -52
  8. mcp_ticketer/adapters/linear/mappers.py +88 -68
  9. mcp_ticketer/adapters/linear/queries.py +28 -7
  10. mcp_ticketer/adapters/linear/types.py +57 -50
  11. mcp_ticketer/adapters/linear.py +2 -2
  12. mcp_ticketer/cli/adapter_diagnostics.py +86 -51
  13. mcp_ticketer/cli/diagnostics.py +165 -72
  14. mcp_ticketer/cli/linear_commands.py +156 -113
  15. mcp_ticketer/cli/main.py +153 -82
  16. mcp_ticketer/cli/simple_health.py +73 -45
  17. mcp_ticketer/cli/utils.py +15 -10
  18. mcp_ticketer/core/config.py +23 -19
  19. mcp_ticketer/core/env_discovery.py +5 -4
  20. mcp_ticketer/core/env_loader.py +109 -86
  21. mcp_ticketer/core/exceptions.py +20 -18
  22. mcp_ticketer/core/models.py +9 -0
  23. mcp_ticketer/core/project_config.py +1 -1
  24. mcp_ticketer/mcp/server.py +294 -139
  25. mcp_ticketer/queue/health_monitor.py +152 -121
  26. mcp_ticketer/queue/manager.py +11 -4
  27. mcp_ticketer/queue/queue.py +15 -3
  28. mcp_ticketer/queue/run_worker.py +1 -1
  29. mcp_ticketer/queue/ticket_registry.py +190 -132
  30. mcp_ticketer/queue/worker.py +54 -25
  31. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/METADATA +1 -1
  32. mcp_ticketer-0.3.2.dist-info/RECORD +59 -0
  33. mcp_ticketer-0.3.1.dist-info/RECORD +0 -59
  34. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/WHEEL +0 -0
  35. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/entry_points.txt +0 -0
  36. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.dist-info}/licenses/LICENSE +0 -0
  37. {mcp_ticketer-0.3.1.dist-info → mcp_ticketer-0.3.2.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, 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
- 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()
@@ -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": 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
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(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)
@@ -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, '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)
@@ -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": {"attempted": False, "success": False, "error": None},
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(f"✅ Queue worker running (PID: {queue_status['worker_pid']})")
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(f"❌ Failed to start queue worker: {start_test['error']}")
357
- self.issues.append(f"Queue worker startup failed: {start_test['error']}")
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(f"✅ Queue failure rate: {failure_rate:.1f}% ({failed_items}/{total_items})")
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(f"❌ Queue operations test failed: {operation_test['error']}")
392
- self.issues.append(f"Queue operations failed: {operation_test['error']}")
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 not queue_status["worker_start_test"]["success"] and queue_status["worker_start_test"]["attempted"]:
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(f"📊 Queue health score: {queue_status['health_score']}/100 (based on active testing)")
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, 'start'):
468
+ if hasattr(self.worker_manager, "start"):
426
469
  result = self.worker_manager.start()
427
470
  test_result["success"] = result
428
- test_result["details"] = "Worker started successfully" if result else "Worker failed to start"
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 Task, Priority
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(f"✅ Found logs in {len(log_analysis['log_files_found'])} location(s)")
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(self, log_path: Path, log_analysis: Dict[str, Any]):
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 log_file.stat().st_mtime > (datetime.now() - timedelta(hours=24)).timestamp():
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, 'r') as f:
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("🚨 Critical issues detected - immediate attention required")
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("• Restart queue worker: mcp-ticketer queue worker restart")
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("• Consider clearing failed queue items: mcp-ticketer queue clear --failed")
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("• Configure at least one adapter: mcp-ticketer init-aitrackdown")
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("✅ System appears healthy - no immediate action required")
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(f"\n{status_icon} [bold {status_color}]System Status: {status_text}[/bold {status_color}]")
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 = "✅ 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")
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(f"\n🚨 [bold red]Critical Issues ({len(self.issues)}):[/bold red]")
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(f"\n⚠️ [bold yellow]Warnings ({len(self.warnings)}):[/bold yellow]")
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['recommendations']:
698
- console.print(f"\n💡 [bold blue]Recommendations:[/bold blue]")
699
- for rec in report['recommendations']:
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(f"\n📊 [bold]Summary:[/bold] {len(self.successes)} successes, {len(self.warnings)} warnings, {len(self.issues)} critical issues")
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, 'w') as f:
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