mcp-ticketer 0.2.0__py3-none-any.whl → 2.2.9__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.
Files changed (160) hide show
  1. mcp_ticketer/__init__.py +10 -10
  2. mcp_ticketer/__version__.py +3 -3
  3. mcp_ticketer/_version_scm.py +1 -0
  4. mcp_ticketer/adapters/__init__.py +2 -0
  5. mcp_ticketer/adapters/aitrackdown.py +930 -52
  6. mcp_ticketer/adapters/asana/__init__.py +15 -0
  7. mcp_ticketer/adapters/asana/adapter.py +1537 -0
  8. mcp_ticketer/adapters/asana/client.py +292 -0
  9. mcp_ticketer/adapters/asana/mappers.py +348 -0
  10. mcp_ticketer/adapters/asana/types.py +146 -0
  11. mcp_ticketer/adapters/github/__init__.py +26 -0
  12. mcp_ticketer/adapters/github/adapter.py +3229 -0
  13. mcp_ticketer/adapters/github/client.py +335 -0
  14. mcp_ticketer/adapters/github/mappers.py +797 -0
  15. mcp_ticketer/adapters/github/queries.py +692 -0
  16. mcp_ticketer/adapters/github/types.py +460 -0
  17. mcp_ticketer/adapters/hybrid.py +58 -16
  18. mcp_ticketer/adapters/jira/__init__.py +35 -0
  19. mcp_ticketer/adapters/jira/adapter.py +1351 -0
  20. mcp_ticketer/adapters/jira/client.py +271 -0
  21. mcp_ticketer/adapters/jira/mappers.py +246 -0
  22. mcp_ticketer/adapters/jira/queries.py +216 -0
  23. mcp_ticketer/adapters/jira/types.py +304 -0
  24. mcp_ticketer/adapters/linear/__init__.py +1 -1
  25. mcp_ticketer/adapters/linear/adapter.py +3810 -462
  26. mcp_ticketer/adapters/linear/client.py +312 -69
  27. mcp_ticketer/adapters/linear/mappers.py +305 -85
  28. mcp_ticketer/adapters/linear/queries.py +317 -17
  29. mcp_ticketer/adapters/linear/types.py +187 -64
  30. mcp_ticketer/adapters/linear.py +2 -2
  31. mcp_ticketer/analysis/__init__.py +56 -0
  32. mcp_ticketer/analysis/dependency_graph.py +255 -0
  33. mcp_ticketer/analysis/health_assessment.py +304 -0
  34. mcp_ticketer/analysis/orphaned.py +218 -0
  35. mcp_ticketer/analysis/project_status.py +594 -0
  36. mcp_ticketer/analysis/similarity.py +224 -0
  37. mcp_ticketer/analysis/staleness.py +266 -0
  38. mcp_ticketer/automation/__init__.py +11 -0
  39. mcp_ticketer/automation/project_updates.py +378 -0
  40. mcp_ticketer/cache/memory.py +9 -8
  41. mcp_ticketer/cli/adapter_diagnostics.py +421 -0
  42. mcp_ticketer/cli/auggie_configure.py +116 -15
  43. mcp_ticketer/cli/codex_configure.py +274 -82
  44. mcp_ticketer/cli/configure.py +1323 -151
  45. mcp_ticketer/cli/cursor_configure.py +314 -0
  46. mcp_ticketer/cli/diagnostics.py +209 -114
  47. mcp_ticketer/cli/discover.py +297 -26
  48. mcp_ticketer/cli/gemini_configure.py +119 -26
  49. mcp_ticketer/cli/init_command.py +880 -0
  50. mcp_ticketer/cli/install_mcp_server.py +418 -0
  51. mcp_ticketer/cli/instruction_commands.py +435 -0
  52. mcp_ticketer/cli/linear_commands.py +256 -130
  53. mcp_ticketer/cli/main.py +140 -1284
  54. mcp_ticketer/cli/mcp_configure.py +1013 -100
  55. mcp_ticketer/cli/mcp_server_commands.py +415 -0
  56. mcp_ticketer/cli/migrate_config.py +12 -8
  57. mcp_ticketer/cli/platform_commands.py +123 -0
  58. mcp_ticketer/cli/platform_detection.py +477 -0
  59. mcp_ticketer/cli/platform_installer.py +545 -0
  60. mcp_ticketer/cli/project_update_commands.py +350 -0
  61. mcp_ticketer/cli/python_detection.py +126 -0
  62. mcp_ticketer/cli/queue_commands.py +15 -15
  63. mcp_ticketer/cli/setup_command.py +794 -0
  64. mcp_ticketer/cli/simple_health.py +84 -59
  65. mcp_ticketer/cli/ticket_commands.py +1375 -0
  66. mcp_ticketer/cli/update_checker.py +313 -0
  67. mcp_ticketer/cli/utils.py +195 -72
  68. mcp_ticketer/core/__init__.py +64 -1
  69. mcp_ticketer/core/adapter.py +618 -18
  70. mcp_ticketer/core/config.py +77 -68
  71. mcp_ticketer/core/env_discovery.py +75 -16
  72. mcp_ticketer/core/env_loader.py +121 -97
  73. mcp_ticketer/core/exceptions.py +32 -24
  74. mcp_ticketer/core/http_client.py +26 -26
  75. mcp_ticketer/core/instructions.py +405 -0
  76. mcp_ticketer/core/label_manager.py +732 -0
  77. mcp_ticketer/core/mappers.py +42 -30
  78. mcp_ticketer/core/milestone_manager.py +252 -0
  79. mcp_ticketer/core/models.py +566 -19
  80. mcp_ticketer/core/onepassword_secrets.py +379 -0
  81. mcp_ticketer/core/priority_matcher.py +463 -0
  82. mcp_ticketer/core/project_config.py +189 -49
  83. mcp_ticketer/core/project_utils.py +281 -0
  84. mcp_ticketer/core/project_validator.py +376 -0
  85. mcp_ticketer/core/registry.py +3 -3
  86. mcp_ticketer/core/session_state.py +176 -0
  87. mcp_ticketer/core/state_matcher.py +592 -0
  88. mcp_ticketer/core/url_parser.py +425 -0
  89. mcp_ticketer/core/validators.py +69 -0
  90. mcp_ticketer/defaults/ticket_instructions.md +644 -0
  91. mcp_ticketer/mcp/__init__.py +29 -1
  92. mcp_ticketer/mcp/__main__.py +60 -0
  93. mcp_ticketer/mcp/server/__init__.py +25 -0
  94. mcp_ticketer/mcp/server/__main__.py +60 -0
  95. mcp_ticketer/mcp/server/constants.py +58 -0
  96. mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
  97. mcp_ticketer/mcp/server/dto.py +195 -0
  98. mcp_ticketer/mcp/server/main.py +1343 -0
  99. mcp_ticketer/mcp/server/response_builder.py +206 -0
  100. mcp_ticketer/mcp/server/routing.py +723 -0
  101. mcp_ticketer/mcp/server/server_sdk.py +151 -0
  102. mcp_ticketer/mcp/server/tools/__init__.py +69 -0
  103. mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
  104. mcp_ticketer/mcp/server/tools/attachment_tools.py +224 -0
  105. mcp_ticketer/mcp/server/tools/bulk_tools.py +330 -0
  106. mcp_ticketer/mcp/server/tools/comment_tools.py +152 -0
  107. mcp_ticketer/mcp/server/tools/config_tools.py +1564 -0
  108. mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
  109. mcp_ticketer/mcp/server/tools/hierarchy_tools.py +942 -0
  110. mcp_ticketer/mcp/server/tools/instruction_tools.py +295 -0
  111. mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
  112. mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
  113. mcp_ticketer/mcp/server/tools/pr_tools.py +150 -0
  114. mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
  115. mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
  116. mcp_ticketer/mcp/server/tools/search_tools.py +318 -0
  117. mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
  118. mcp_ticketer/mcp/server/tools/ticket_tools.py +1413 -0
  119. mcp_ticketer/mcp/server/tools/user_ticket_tools.py +364 -0
  120. mcp_ticketer/queue/__init__.py +1 -0
  121. mcp_ticketer/queue/health_monitor.py +168 -136
  122. mcp_ticketer/queue/manager.py +78 -63
  123. mcp_ticketer/queue/queue.py +108 -21
  124. mcp_ticketer/queue/run_worker.py +2 -2
  125. mcp_ticketer/queue/ticket_registry.py +213 -155
  126. mcp_ticketer/queue/worker.py +96 -58
  127. mcp_ticketer/utils/__init__.py +5 -0
  128. mcp_ticketer/utils/token_utils.py +246 -0
  129. mcp_ticketer-2.2.9.dist-info/METADATA +1396 -0
  130. mcp_ticketer-2.2.9.dist-info/RECORD +158 -0
  131. mcp_ticketer-2.2.9.dist-info/top_level.txt +2 -0
  132. py_mcp_installer/examples/phase3_demo.py +178 -0
  133. py_mcp_installer/scripts/manage_version.py +54 -0
  134. py_mcp_installer/setup.py +6 -0
  135. py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
  136. py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
  137. py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
  138. py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
  139. py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
  140. py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
  141. py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
  142. py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
  143. py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
  144. py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
  145. py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
  146. py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
  147. py_mcp_installer/src/py_mcp_installer/types.py +222 -0
  148. py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
  149. py_mcp_installer/tests/__init__.py +0 -0
  150. py_mcp_installer/tests/platforms/__init__.py +0 -0
  151. py_mcp_installer/tests/test_platform_detector.py +17 -0
  152. mcp_ticketer/adapters/github.py +0 -1354
  153. mcp_ticketer/adapters/jira.py +0 -1011
  154. mcp_ticketer/mcp/server.py +0 -1895
  155. mcp_ticketer-0.2.0.dist-info/METADATA +0 -414
  156. mcp_ticketer-0.2.0.dist-info/RECORD +0 -58
  157. mcp_ticketer-0.2.0.dist-info/top_level.txt +0 -1
  158. {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-2.2.9.dist-info}/WHEEL +0 -0
  159. {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-2.2.9.dist-info}/entry_points.txt +0 -0
  160. {mcp_ticketer-0.2.0.dist-info → mcp_ticketer-2.2.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,40 +1,42 @@
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
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
17
13
 
18
- def get_config():
14
+
15
+ def get_config() -> Any:
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
 
24
- def safe_import_registry():
22
+
23
+ def safe_import_registry() -> type:
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
- def get_adapter(adapter_type):
33
+ def get_adapter(adapter_type: str) -> None:
33
34
  raise ImportError(f"Adapter {adapter_type} not available")
34
35
 
35
36
  return MockRegistry
36
37
 
37
- def safe_import_queue_manager():
38
+
39
+ def safe_import_queue_manager() -> type:
38
40
  """Safely import worker manager with fallback."""
39
41
  try:
40
42
  from ..queue.manager import WorkerManager as RealWorkerManager
@@ -53,20 +55,25 @@ def safe_import_queue_manager():
53
55
  pass
54
56
 
55
57
  class MockWorkerManager:
56
- def get_status(self):
58
+ def get_status(self) -> dict[str, Any]:
57
59
  return {"running": False, "pid": None, "status": "fallback_mode"}
58
60
 
59
- def get_worker_status(self):
61
+ def get_worker_status(self) -> dict[str, Any]:
60
62
  return {"running": False, "pid": None, "status": "fallback_mode"}
61
63
 
62
- def get_queue_stats(self):
64
+ def get_queue_stats(self) -> dict[str, Any]:
63
65
  return {"total": 0, "failed": 0, "pending": 0, "completed": 0}
64
66
 
65
- def health_check(self):
66
- return {"status": "degraded", "score": 50, "details": "Running in fallback mode"}
67
+ def health_check(self) -> dict[str, Any]:
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()
@@ -78,11 +85,11 @@ logger = logging.getLogger(__name__)
78
85
  class SystemDiagnostics:
79
86
  """Comprehensive system diagnostics and health reporting."""
80
87
 
81
- def __init__(self):
88
+ def __init__(self) -> None:
82
89
  # Initialize lists first
83
- self.issues = []
84
- self.warnings = []
85
- self.successes = []
90
+ self.issues: list[str] = []
91
+ self.warnings: list[str] = []
92
+ self.successes: list[str] = []
86
93
 
87
94
  try:
88
95
  self.config = get_config()
@@ -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,24 +132,29 @@ 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
 
145
- config_status = {
157
+ config_status: dict[str, Any] = {
146
158
  "status": "healthy",
147
159
  "adapters_configured": 0,
148
160
  "default_adapter": None,
@@ -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,11 +256,11 @@ 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
-
243
- adapter_status = {
262
+
263
+ adapter_status: dict[str, Any] = {
244
264
  "total_adapters": 0,
245
265
  "healthy_adapters": 0,
246
266
  "failed_adapters": 0,
@@ -250,15 +270,15 @@ 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
- details = {
281
+ details: dict[str, Any] = {
262
282
  "type": adapter_type,
263
283
  "status": "unknown",
264
284
  "last_test": None,
@@ -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,11 +326,11 @@ 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
 
312
- queue_status = {
333
+ queue_status: dict[str, Any] = {
313
334
  "worker_running": False,
314
335
  "worker_pid": None,
315
336
  "queue_stats": {},
@@ -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
- test_result = {
458
+ test_result: dict[str, Any] = {
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
- test_result = {
500
+ test_result: dict[str, Any] = {
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
- test_task = Task(
512
+ test_task = Task( # type: ignore[call-arg]
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
- test_result = {
535
+ test_result: dict[str, Any] = {
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
- test_task = Task(
547
+ test_task = Task( # type: ignore[call-arg]
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)
@@ -517,18 +564,19 @@ class SystemDiagnostics:
517
564
  test_result["details"] = f"Direct adapter test passed: {result.id}"
518
565
 
519
566
  # Clean up test
520
- await adapter.delete(result.id)
567
+ if result.id:
568
+ await adapter.delete(result.id)
521
569
 
522
570
  except Exception as e:
523
571
  test_result["error"] = str(e)
524
572
 
525
573
  return test_result
526
574
 
527
- async def _analyze_recent_logs(self) -> Dict[str, Any]:
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
-
531
- log_analysis = {
578
+
579
+ log_analysis: dict[str, Any] = {
532
580
  "log_files_found": [],
533
581
  "recent_errors": [],
534
582
  "recent_warnings": [],
@@ -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,21 +610,28 @@ 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
+ ) -> None:
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)}")
571
626
 
572
- async def _parse_log_file(self, log_file: Path, log_analysis: Dict[str, Any]):
627
+ async def _parse_log_file(
628
+ self, log_file: Path, log_analysis: dict[str, Any]
629
+ ) -> None:
573
630
  """Parse individual log file for issues."""
574
631
  try:
575
- with open(log_file, 'r') as f:
632
+ with open(log_file) as f:
576
633
  lines = f.readlines()[-100:] # Last 100 lines
577
-
634
+
578
635
  for line in lines:
579
636
  if "ERROR" in line:
580
637
  log_analysis["recent_errors"].append(line.strip())
@@ -584,11 +641,11 @@ class SystemDiagnostics:
584
641
  except Exception as e:
585
642
  self.warnings.append(f"Could not parse {log_file}: {str(e)}")
586
643
 
587
- async def _analyze_performance(self) -> Dict[str, Any]:
644
+ async def _analyze_performance(self) -> dict[str, Any]:
588
645
  """Analyze system performance metrics."""
589
646
  console.print("\n⚔ [yellow]Performance Analysis[/yellow]")
590
-
591
- performance = {
647
+
648
+ performance: dict[str, Any] = {
592
649
  "response_times": {},
593
650
  "throughput": {},
594
651
  "resource_usage": {},
@@ -596,17 +653,17 @@ class SystemDiagnostics:
596
653
 
597
654
  try:
598
655
  # Test basic operations performance
599
- start_time = datetime.now()
600
-
656
+ datetime.now()
657
+
601
658
  # Test configuration loading
602
659
  config_start = datetime.now()
603
660
  _ = get_config()
604
661
  config_time = (datetime.now() - config_start).total_seconds()
605
662
  performance["response_times"]["config_load"] = config_time
606
-
663
+
607
664
  if config_time > 1.0:
608
665
  self.warnings.append(f"Slow configuration loading: {config_time:.2f}s")
609
-
666
+
610
667
  console.print(f"šŸ“Š Configuration load time: {config_time:.3f}s")
611
668
 
612
669
  except Exception as e:
@@ -616,32 +673,42 @@ class SystemDiagnostics:
616
673
 
617
674
  return performance
618
675
 
619
- def _generate_recommendations(self) -> List[str]:
676
+ def _generate_recommendations(self) -> list[str]:
620
677
  """Generate actionable recommendations based on diagnosis."""
621
- recommendations = []
678
+ recommendations: list[str] = []
622
679
 
623
680
  if self.issues:
624
- recommendations.append("🚨 Critical issues detected - immediate attention required")
625
-
681
+ recommendations.append(
682
+ "🚨 Critical issues detected - immediate attention required"
683
+ )
684
+
626
685
  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
-
686
+ recommendations.append(
687
+ "• Restart queue worker: mcp-ticketer queue worker restart"
688
+ )
689
+
629
690
  if any("failure rate" in issue.lower() for issue in self.issues):
630
691
  recommendations.append("• Check queue system logs for error patterns")
631
- recommendations.append("• Consider clearing failed queue items: mcp-ticketer queue clear --failed")
632
-
692
+ recommendations.append(
693
+ "• Consider clearing failed queue items: mcp-ticketer queue clear --failed"
694
+ )
695
+
633
696
  if any("No adapters configured" in issue for issue in self.issues):
634
- recommendations.append("• Configure at least one adapter: mcp-ticketer init-aitrackdown")
697
+ recommendations.append(
698
+ "• Configure at least one adapter: mcp-ticketer init-aitrackdown"
699
+ )
635
700
 
636
701
  if self.warnings:
637
702
  recommendations.append("āš ļø Warnings detected - monitoring recommended")
638
703
 
639
704
  if not self.issues and not self.warnings:
640
- recommendations.append("āœ… System appears healthy - no immediate action required")
705
+ recommendations.append(
706
+ "āœ… System appears healthy - no immediate action required"
707
+ )
641
708
 
642
709
  return recommendations
643
710
 
644
- def _display_diagnosis_summary(self, report: Dict[str, Any]):
711
+ def _display_diagnosis_summary(self, report: dict[str, Any]) -> None:
645
712
  """Display a comprehensive diagnosis summary."""
646
713
  console.print("\n" + "=" * 60)
647
714
  console.print("šŸ“‹ [bold green]DIAGNOSIS SUMMARY[/bold green]")
@@ -661,7 +728,9 @@ class SystemDiagnostics:
661
728
  status_text = "HEALTHY"
662
729
  status_icon = "āœ…"
663
730
 
664
- console.print(f"\n{status_icon} [bold {status_color}]System Status: {status_text}[/bold {status_color}]")
731
+ console.print(
732
+ f"\n{status_icon} [bold {status_color}]System Status: {status_text}[/bold {status_color}]"
733
+ )
665
734
 
666
735
  # Statistics
667
736
  stats_table = Table(show_header=True, header_style="bold blue")
@@ -670,40 +739,66 @@ class SystemDiagnostics:
670
739
  stats_table.add_column("Details")
671
740
 
672
741
  # 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")
742
+ config_status = (
743
+ "āœ… OK"
744
+ if not any("configuration" in issue.lower() for issue in self.issues)
745
+ else "āŒ FAILED"
746
+ )
747
+ stats_table.add_row(
748
+ "Configuration",
749
+ config_status,
750
+ f"{report['configuration']['adapters_configured']} adapters",
751
+ )
752
+
753
+ queue_health = report["queue_system"]["health_score"]
754
+ queue_status = (
755
+ "āœ… OK"
756
+ if queue_health > 80
757
+ else "āš ļø DEGRADED" if queue_health > 50 else "āŒ FAILED"
758
+ )
759
+ stats_table.add_row(
760
+ "Queue System", queue_status, f"{queue_health}/100 health score"
761
+ )
762
+
763
+ adapter_stats = report["adapters"]
764
+ adapter_status = (
765
+ "āœ… OK" if adapter_stats["failed_adapters"] == 0 else "āŒ FAILED"
766
+ )
767
+ stats_table.add_row(
768
+ "Adapters",
769
+ adapter_status,
770
+ f"{adapter_stats['healthy_adapters']}/{adapter_stats['total_adapters']} healthy",
771
+ )
683
772
 
684
773
  console.print(stats_table)
685
774
 
686
775
  # Issues and recommendations
687
776
  if self.issues:
688
- console.print(f"\n🚨 [bold red]Critical Issues ({len(self.issues)}):[/bold red]")
777
+ console.print(
778
+ f"\n🚨 [bold red]Critical Issues ({len(self.issues)}):[/bold red]"
779
+ )
689
780
  for issue in self.issues:
690
781
  console.print(f" • {issue}")
691
782
 
692
783
  if self.warnings:
693
- console.print(f"\nāš ļø [bold yellow]Warnings ({len(self.warnings)}):[/bold yellow]")
784
+ console.print(
785
+ f"\nāš ļø [bold yellow]Warnings ({len(self.warnings)}):[/bold yellow]"
786
+ )
694
787
  for warning in self.warnings:
695
788
  console.print(f" • {warning}")
696
789
 
697
- if report['recommendations']:
698
- console.print(f"\nšŸ’” [bold blue]Recommendations:[/bold blue]")
699
- for rec in report['recommendations']:
790
+ if report["recommendations"]:
791
+ console.print("\nšŸ’” [bold blue]Recommendations:[/bold blue]")
792
+ for rec in report["recommendations"]:
700
793
  console.print(f" {rec}")
701
794
 
702
- console.print(f"\nšŸ“Š [bold]Summary:[/bold] {len(self.successes)} successes, {len(self.warnings)} warnings, {len(self.issues)} critical issues")
795
+ console.print(
796
+ f"\nšŸ“Š [bold]Summary:[/bold] {len(self.successes)} successes, {len(self.warnings)} warnings, {len(self.issues)} critical issues"
797
+ )
703
798
 
704
799
 
705
800
  async def run_diagnostics(
706
- output_file: Optional[str] = None,
801
+ output_file: str | None = None,
707
802
  json_output: bool = False,
708
803
  ) -> None:
709
804
  """Run comprehensive system diagnostics."""
@@ -711,7 +806,7 @@ async def run_diagnostics(
711
806
  report = await diagnostics.run_full_diagnosis()
712
807
 
713
808
  if output_file:
714
- with open(output_file, 'w') as f:
809
+ with open(output_file, "w") as f:
715
810
  json.dump(report, f, indent=2)
716
811
  console.print(f"\nšŸ“„ Full report saved to: {output_file}")
717
812