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.
- mcp_ticketer/__init__.py +10 -10
- mcp_ticketer/__version__.py +3 -3
- mcp_ticketer/_version_scm.py +1 -0
- mcp_ticketer/adapters/__init__.py +2 -0
- mcp_ticketer/adapters/aitrackdown.py +930 -52
- mcp_ticketer/adapters/asana/__init__.py +15 -0
- mcp_ticketer/adapters/asana/adapter.py +1537 -0
- mcp_ticketer/adapters/asana/client.py +292 -0
- mcp_ticketer/adapters/asana/mappers.py +348 -0
- mcp_ticketer/adapters/asana/types.py +146 -0
- mcp_ticketer/adapters/github/__init__.py +26 -0
- mcp_ticketer/adapters/github/adapter.py +3229 -0
- mcp_ticketer/adapters/github/client.py +335 -0
- mcp_ticketer/adapters/github/mappers.py +797 -0
- mcp_ticketer/adapters/github/queries.py +692 -0
- mcp_ticketer/adapters/github/types.py +460 -0
- mcp_ticketer/adapters/hybrid.py +58 -16
- mcp_ticketer/adapters/jira/__init__.py +35 -0
- mcp_ticketer/adapters/jira/adapter.py +1351 -0
- mcp_ticketer/adapters/jira/client.py +271 -0
- mcp_ticketer/adapters/jira/mappers.py +246 -0
- mcp_ticketer/adapters/jira/queries.py +216 -0
- mcp_ticketer/adapters/jira/types.py +304 -0
- mcp_ticketer/adapters/linear/__init__.py +1 -1
- mcp_ticketer/adapters/linear/adapter.py +3810 -462
- mcp_ticketer/adapters/linear/client.py +312 -69
- mcp_ticketer/adapters/linear/mappers.py +305 -85
- mcp_ticketer/adapters/linear/queries.py +317 -17
- mcp_ticketer/adapters/linear/types.py +187 -64
- mcp_ticketer/adapters/linear.py +2 -2
- mcp_ticketer/analysis/__init__.py +56 -0
- mcp_ticketer/analysis/dependency_graph.py +255 -0
- mcp_ticketer/analysis/health_assessment.py +304 -0
- mcp_ticketer/analysis/orphaned.py +218 -0
- mcp_ticketer/analysis/project_status.py +594 -0
- mcp_ticketer/analysis/similarity.py +224 -0
- mcp_ticketer/analysis/staleness.py +266 -0
- mcp_ticketer/automation/__init__.py +11 -0
- mcp_ticketer/automation/project_updates.py +378 -0
- mcp_ticketer/cache/memory.py +9 -8
- mcp_ticketer/cli/adapter_diagnostics.py +421 -0
- mcp_ticketer/cli/auggie_configure.py +116 -15
- mcp_ticketer/cli/codex_configure.py +274 -82
- mcp_ticketer/cli/configure.py +1323 -151
- mcp_ticketer/cli/cursor_configure.py +314 -0
- mcp_ticketer/cli/diagnostics.py +209 -114
- mcp_ticketer/cli/discover.py +297 -26
- mcp_ticketer/cli/gemini_configure.py +119 -26
- mcp_ticketer/cli/init_command.py +880 -0
- mcp_ticketer/cli/install_mcp_server.py +418 -0
- mcp_ticketer/cli/instruction_commands.py +435 -0
- mcp_ticketer/cli/linear_commands.py +256 -130
- mcp_ticketer/cli/main.py +140 -1284
- mcp_ticketer/cli/mcp_configure.py +1013 -100
- mcp_ticketer/cli/mcp_server_commands.py +415 -0
- mcp_ticketer/cli/migrate_config.py +12 -8
- mcp_ticketer/cli/platform_commands.py +123 -0
- mcp_ticketer/cli/platform_detection.py +477 -0
- mcp_ticketer/cli/platform_installer.py +545 -0
- mcp_ticketer/cli/project_update_commands.py +350 -0
- mcp_ticketer/cli/python_detection.py +126 -0
- mcp_ticketer/cli/queue_commands.py +15 -15
- mcp_ticketer/cli/setup_command.py +794 -0
- mcp_ticketer/cli/simple_health.py +84 -59
- mcp_ticketer/cli/ticket_commands.py +1375 -0
- mcp_ticketer/cli/update_checker.py +313 -0
- mcp_ticketer/cli/utils.py +195 -72
- mcp_ticketer/core/__init__.py +64 -1
- mcp_ticketer/core/adapter.py +618 -18
- mcp_ticketer/core/config.py +77 -68
- mcp_ticketer/core/env_discovery.py +75 -16
- mcp_ticketer/core/env_loader.py +121 -97
- mcp_ticketer/core/exceptions.py +32 -24
- mcp_ticketer/core/http_client.py +26 -26
- mcp_ticketer/core/instructions.py +405 -0
- mcp_ticketer/core/label_manager.py +732 -0
- mcp_ticketer/core/mappers.py +42 -30
- mcp_ticketer/core/milestone_manager.py +252 -0
- mcp_ticketer/core/models.py +566 -19
- mcp_ticketer/core/onepassword_secrets.py +379 -0
- mcp_ticketer/core/priority_matcher.py +463 -0
- mcp_ticketer/core/project_config.py +189 -49
- mcp_ticketer/core/project_utils.py +281 -0
- mcp_ticketer/core/project_validator.py +376 -0
- mcp_ticketer/core/registry.py +3 -3
- mcp_ticketer/core/session_state.py +176 -0
- mcp_ticketer/core/state_matcher.py +592 -0
- mcp_ticketer/core/url_parser.py +425 -0
- mcp_ticketer/core/validators.py +69 -0
- mcp_ticketer/defaults/ticket_instructions.md +644 -0
- mcp_ticketer/mcp/__init__.py +29 -1
- mcp_ticketer/mcp/__main__.py +60 -0
- mcp_ticketer/mcp/server/__init__.py +25 -0
- mcp_ticketer/mcp/server/__main__.py +60 -0
- mcp_ticketer/mcp/server/constants.py +58 -0
- mcp_ticketer/mcp/server/diagnostic_helper.py +175 -0
- mcp_ticketer/mcp/server/dto.py +195 -0
- mcp_ticketer/mcp/server/main.py +1343 -0
- mcp_ticketer/mcp/server/response_builder.py +206 -0
- mcp_ticketer/mcp/server/routing.py +723 -0
- mcp_ticketer/mcp/server/server_sdk.py +151 -0
- mcp_ticketer/mcp/server/tools/__init__.py +69 -0
- mcp_ticketer/mcp/server/tools/analysis_tools.py +854 -0
- mcp_ticketer/mcp/server/tools/attachment_tools.py +224 -0
- mcp_ticketer/mcp/server/tools/bulk_tools.py +330 -0
- mcp_ticketer/mcp/server/tools/comment_tools.py +152 -0
- mcp_ticketer/mcp/server/tools/config_tools.py +1564 -0
- mcp_ticketer/mcp/server/tools/diagnostic_tools.py +211 -0
- mcp_ticketer/mcp/server/tools/hierarchy_tools.py +942 -0
- mcp_ticketer/mcp/server/tools/instruction_tools.py +295 -0
- mcp_ticketer/mcp/server/tools/label_tools.py +942 -0
- mcp_ticketer/mcp/server/tools/milestone_tools.py +338 -0
- mcp_ticketer/mcp/server/tools/pr_tools.py +150 -0
- mcp_ticketer/mcp/server/tools/project_status_tools.py +158 -0
- mcp_ticketer/mcp/server/tools/project_update_tools.py +473 -0
- mcp_ticketer/mcp/server/tools/search_tools.py +318 -0
- mcp_ticketer/mcp/server/tools/session_tools.py +308 -0
- mcp_ticketer/mcp/server/tools/ticket_tools.py +1413 -0
- mcp_ticketer/mcp/server/tools/user_ticket_tools.py +364 -0
- mcp_ticketer/queue/__init__.py +1 -0
- mcp_ticketer/queue/health_monitor.py +168 -136
- mcp_ticketer/queue/manager.py +78 -63
- mcp_ticketer/queue/queue.py +108 -21
- mcp_ticketer/queue/run_worker.py +2 -2
- mcp_ticketer/queue/ticket_registry.py +213 -155
- mcp_ticketer/queue/worker.py +96 -58
- mcp_ticketer/utils/__init__.py +5 -0
- mcp_ticketer/utils/token_utils.py +246 -0
- mcp_ticketer-2.2.9.dist-info/METADATA +1396 -0
- mcp_ticketer-2.2.9.dist-info/RECORD +158 -0
- mcp_ticketer-2.2.9.dist-info/top_level.txt +2 -0
- py_mcp_installer/examples/phase3_demo.py +178 -0
- py_mcp_installer/scripts/manage_version.py +54 -0
- py_mcp_installer/setup.py +6 -0
- py_mcp_installer/src/py_mcp_installer/__init__.py +153 -0
- py_mcp_installer/src/py_mcp_installer/command_builder.py +445 -0
- py_mcp_installer/src/py_mcp_installer/config_manager.py +541 -0
- py_mcp_installer/src/py_mcp_installer/exceptions.py +243 -0
- py_mcp_installer/src/py_mcp_installer/installation_strategy.py +617 -0
- py_mcp_installer/src/py_mcp_installer/installer.py +656 -0
- py_mcp_installer/src/py_mcp_installer/mcp_inspector.py +750 -0
- py_mcp_installer/src/py_mcp_installer/platform_detector.py +451 -0
- py_mcp_installer/src/py_mcp_installer/platforms/__init__.py +26 -0
- py_mcp_installer/src/py_mcp_installer/platforms/claude_code.py +225 -0
- py_mcp_installer/src/py_mcp_installer/platforms/codex.py +181 -0
- py_mcp_installer/src/py_mcp_installer/platforms/cursor.py +191 -0
- py_mcp_installer/src/py_mcp_installer/types.py +222 -0
- py_mcp_installer/src/py_mcp_installer/utils.py +463 -0
- py_mcp_installer/tests/__init__.py +0 -0
- py_mcp_installer/tests/platforms/__init__.py +0 -0
- py_mcp_installer/tests/test_platform_detector.py +17 -0
- mcp_ticketer/adapters/github.py +0 -1354
- mcp_ticketer/adapters/jira.py +0 -1011
- mcp_ticketer/mcp/server.py +0 -1895
- mcp_ticketer-0.2.0.dist-info/METADATA +0 -414
- mcp_ticketer-0.2.0.dist-info/RECORD +0 -58
- mcp_ticketer-0.2.0.dist-info/top_level.txt +0 -1
- {mcp_ticketer-0.2.0.dist-info ā mcp_ticketer-2.2.9.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.2.0.dist-info ā mcp_ticketer-2.2.9.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.2.0.dist-info ā mcp_ticketer-2.2.9.dist-info}/licenses/LICENSE +0 -0
mcp_ticketer/cli/diagnostics.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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) ->
|
|
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) ->
|
|
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":
|
|
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) ->
|
|
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(
|
|
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)
|
|
@@ -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,
|
|
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,
|
|
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)
|
|
@@ -236,11 +256,11 @@ class SystemDiagnostics:
|
|
|
236
256
|
|
|
237
257
|
return config_status
|
|
238
258
|
|
|
239
|
-
async def _diagnose_adapters(self) ->
|
|
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) ->
|
|
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": {
|
|
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(
|
|
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(
|
|
357
|
-
|
|
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(
|
|
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(
|
|
392
|
-
|
|
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
|
|
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(
|
|
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) ->
|
|
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,
|
|
467
|
+
if hasattr(self.worker_manager, "start"):
|
|
426
468
|
result = self.worker_manager.start()
|
|
427
469
|
test_result["success"] = result
|
|
428
|
-
test_result["details"] =
|
|
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) ->
|
|
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
|
|
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) ->
|
|
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
|
-
|
|
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) ->
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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) ->
|
|
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
|
-
|
|
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) ->
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
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 =
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
stats_table.add_row(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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(
|
|
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(
|
|
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[
|
|
698
|
-
console.print(
|
|
699
|
-
for rec in report[
|
|
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(
|
|
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:
|
|
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,
|
|
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
|
|