mcp-ticketer 0.3.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 +91 -54
- 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 -1544
- 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 -2030
- mcp_ticketer-0.3.0.dist-info/METADATA +0 -414
- mcp_ticketer-0.3.0.dist-info/RECORD +0 -59
- mcp_ticketer-0.3.0.dist-info/top_level.txt +0 -1
- {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/WHEEL +0 -0
- {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/entry_points.txt +0 -0
- {mcp_ticketer-0.3.0.dist-info → mcp_ticketer-2.2.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
|
|
@@ -14,41 +14,45 @@ def simple_health_check() -> int:
|
|
|
14
14
|
"""Perform a simple health check without heavy dependencies."""
|
|
15
15
|
console.print("\n🏥 [bold blue]MCP Ticketer Quick Health Check[/bold blue]")
|
|
16
16
|
console.print("=" * 50)
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
issues = 0
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
# Check Python version
|
|
21
21
|
python_version = sys.version_info
|
|
22
22
|
if python_version >= (3, 9):
|
|
23
|
-
console.print(
|
|
23
|
+
console.print(
|
|
24
|
+
f"✅ Python: {python_version.major}.{python_version.minor}.{python_version.micro}"
|
|
25
|
+
)
|
|
24
26
|
else:
|
|
25
|
-
console.print(
|
|
27
|
+
console.print(
|
|
28
|
+
f"❌ Python: {python_version.major}.{python_version.minor}.{python_version.micro} (requires 3.9+)"
|
|
29
|
+
)
|
|
26
30
|
issues += 1
|
|
27
|
-
|
|
31
|
+
|
|
28
32
|
# Check for basic configuration files
|
|
29
33
|
config_files = [
|
|
30
34
|
".mcp-ticketer.yaml",
|
|
31
|
-
".mcp-ticketer.yml",
|
|
35
|
+
".mcp-ticketer.yml",
|
|
32
36
|
"mcp-ticketer.yaml",
|
|
33
37
|
"mcp-ticketer.yml",
|
|
34
38
|
".aitrackdown",
|
|
35
39
|
]
|
|
36
|
-
|
|
40
|
+
|
|
37
41
|
config_found = False
|
|
38
42
|
for config_file in config_files:
|
|
39
43
|
if Path(config_file).exists():
|
|
40
44
|
console.print(f"✅ Configuration: Found {config_file}")
|
|
41
45
|
config_found = True
|
|
42
46
|
break
|
|
43
|
-
|
|
47
|
+
|
|
44
48
|
if not config_found:
|
|
45
49
|
console.print("⚠️ Configuration: No config files found (will use defaults)")
|
|
46
|
-
|
|
50
|
+
|
|
47
51
|
# Check for aitrackdown directory (default adapter)
|
|
48
52
|
aitrackdown_path = Path(".aitrackdown")
|
|
49
53
|
if aitrackdown_path.exists():
|
|
50
54
|
console.print(f"✅ Aitrackdown: Directory exists at {aitrackdown_path}")
|
|
51
|
-
|
|
55
|
+
|
|
52
56
|
# Check for tickets
|
|
53
57
|
tickets_dir = aitrackdown_path / "tickets"
|
|
54
58
|
if tickets_dir.exists():
|
|
@@ -58,69 +62,81 @@ def simple_health_check() -> int:
|
|
|
58
62
|
console.print("ℹ️ Aitrackdown: No tickets directory (will be created)")
|
|
59
63
|
else:
|
|
60
64
|
console.print("ℹ️ Aitrackdown: Directory will be created on first use")
|
|
61
|
-
|
|
65
|
+
|
|
62
66
|
# Check environment variables
|
|
63
67
|
env_vars = [
|
|
64
68
|
"LINEAR_API_KEY",
|
|
65
|
-
"LINEAR_TEAM_ID",
|
|
69
|
+
"LINEAR_TEAM_ID",
|
|
66
70
|
"GITHUB_TOKEN",
|
|
67
71
|
"GITHUB_REPO",
|
|
68
72
|
"JIRA_SERVER",
|
|
69
73
|
"JIRA_EMAIL",
|
|
70
74
|
"JIRA_API_TOKEN",
|
|
71
75
|
]
|
|
72
|
-
|
|
76
|
+
|
|
73
77
|
env_found = []
|
|
74
78
|
for var in env_vars:
|
|
75
79
|
if os.getenv(var):
|
|
76
80
|
env_found.append(var)
|
|
77
|
-
|
|
81
|
+
|
|
78
82
|
if env_found:
|
|
79
83
|
console.print(f"✅ Environment: {len(env_found)} adapter variables configured")
|
|
80
84
|
for var in env_found:
|
|
81
85
|
console.print(f" • {var}")
|
|
82
86
|
else:
|
|
83
87
|
console.print("ℹ️ Environment: No adapter variables found (using defaults)")
|
|
84
|
-
|
|
88
|
+
|
|
85
89
|
# Check if we can import core modules
|
|
86
90
|
try:
|
|
87
91
|
import mcp_ticketer
|
|
88
|
-
|
|
92
|
+
|
|
93
|
+
console.print(
|
|
94
|
+
f"✅ Installation: mcp-ticketer {mcp_ticketer.__version__} installed"
|
|
95
|
+
)
|
|
89
96
|
except Exception as e:
|
|
90
97
|
console.print(f"❌ Installation: Import failed - {e}")
|
|
91
98
|
issues += 1
|
|
92
|
-
|
|
99
|
+
|
|
93
100
|
# Try to check queue system (simplified)
|
|
94
101
|
try:
|
|
95
102
|
from ..queue.manager import WorkerManager
|
|
103
|
+
|
|
96
104
|
worker_manager = WorkerManager()
|
|
97
105
|
worker_status = worker_manager.get_status()
|
|
98
106
|
|
|
99
107
|
if worker_status.get("running", False):
|
|
100
108
|
console.print(f"✅ Queue Worker: Running (PID: {worker_status.get('pid')})")
|
|
101
109
|
else:
|
|
102
|
-
console.print(
|
|
110
|
+
console.print(
|
|
111
|
+
"⚠️ Queue Worker: Not running (start with: mcp-ticketer queue worker start)"
|
|
112
|
+
)
|
|
103
113
|
|
|
104
114
|
# Get basic stats
|
|
105
115
|
stats = worker_manager.queue.get_stats()
|
|
106
116
|
total = stats.get("total", 0)
|
|
107
117
|
failed = stats.get("failed", 0)
|
|
108
|
-
|
|
118
|
+
|
|
109
119
|
if total > 0:
|
|
110
120
|
failure_rate = (failed / total) * 100
|
|
111
121
|
if failure_rate > 50:
|
|
112
|
-
console.print(
|
|
122
|
+
console.print(
|
|
123
|
+
f"❌ Queue Health: High failure rate {failure_rate:.1f}% ({failed}/{total})"
|
|
124
|
+
)
|
|
113
125
|
issues += 1
|
|
114
126
|
elif failure_rate > 20:
|
|
115
|
-
console.print(
|
|
127
|
+
console.print(
|
|
128
|
+
f"⚠️ Queue Health: Elevated failure rate {failure_rate:.1f}% ({failed}/{total})"
|
|
129
|
+
)
|
|
116
130
|
else:
|
|
117
|
-
console.print(
|
|
131
|
+
console.print(
|
|
132
|
+
f"✅ Queue Health: {failure_rate:.1f}% failure rate ({failed}/{total})"
|
|
133
|
+
)
|
|
118
134
|
else:
|
|
119
135
|
console.print("ℹ️ Queue Health: No items processed yet")
|
|
120
|
-
|
|
136
|
+
|
|
121
137
|
except Exception as e:
|
|
122
138
|
console.print(f"⚠️ Queue System: Could not check status - {e}")
|
|
123
|
-
|
|
139
|
+
|
|
124
140
|
# Summary
|
|
125
141
|
console.print()
|
|
126
142
|
if issues == 0:
|
|
@@ -133,87 +149,96 @@ def simple_health_check() -> int:
|
|
|
133
149
|
return 1
|
|
134
150
|
|
|
135
151
|
|
|
136
|
-
def simple_diagnose() ->
|
|
137
|
-
"""
|
|
152
|
+
def simple_diagnose() -> dict[str, Any]:
|
|
153
|
+
"""Perform simple diagnosis without full config system."""
|
|
138
154
|
console.print("\n🔍 [bold blue]MCP Ticketer Simple Diagnosis[/bold blue]")
|
|
139
155
|
console.print("=" * 60)
|
|
140
|
-
|
|
156
|
+
|
|
157
|
+
issues: list[str] = []
|
|
158
|
+
warnings: list[str] = []
|
|
159
|
+
recommendations: list[str] = []
|
|
160
|
+
|
|
141
161
|
report = {
|
|
142
162
|
"timestamp": "2025-10-24", # Static for now
|
|
143
163
|
"version": "0.1.28",
|
|
144
164
|
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
|
145
165
|
"working_directory": str(Path.cwd()),
|
|
146
|
-
"issues":
|
|
147
|
-
"warnings":
|
|
148
|
-
"recommendations":
|
|
166
|
+
"issues": issues,
|
|
167
|
+
"warnings": warnings,
|
|
168
|
+
"recommendations": recommendations,
|
|
149
169
|
}
|
|
150
|
-
|
|
170
|
+
|
|
151
171
|
# Basic checks
|
|
152
172
|
console.print("\n📋 [yellow]Basic System Check[/yellow]")
|
|
153
|
-
|
|
173
|
+
|
|
154
174
|
# Python version
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
else:
|
|
160
|
-
console.print(f"✅ Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
|
|
161
|
-
|
|
175
|
+
console.print(
|
|
176
|
+
f"✅ Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
177
|
+
)
|
|
178
|
+
|
|
162
179
|
# Installation check
|
|
163
180
|
try:
|
|
164
181
|
import mcp_ticketer
|
|
182
|
+
|
|
165
183
|
console.print(f"✅ mcp-ticketer {mcp_ticketer.__version__} installed")
|
|
166
184
|
except Exception as e:
|
|
167
185
|
issue = f"Installation check failed: {e}"
|
|
168
|
-
|
|
186
|
+
issues.append(issue)
|
|
169
187
|
console.print(f"❌ {issue}")
|
|
170
|
-
|
|
188
|
+
|
|
171
189
|
# Configuration check
|
|
172
190
|
console.print("\n📋 [yellow]Configuration Check[/yellow]")
|
|
173
|
-
config_files = [
|
|
191
|
+
config_files = [
|
|
192
|
+
".mcp-ticketer.yaml",
|
|
193
|
+
".mcp-ticketer.yml",
|
|
194
|
+
"mcp-ticketer.yaml",
|
|
195
|
+
"mcp-ticketer.yml",
|
|
196
|
+
]
|
|
174
197
|
config_found = any(Path(f).exists() for f in config_files)
|
|
175
|
-
|
|
198
|
+
|
|
176
199
|
if config_found:
|
|
177
200
|
console.print("✅ Configuration files found")
|
|
178
201
|
else:
|
|
179
202
|
console.print("ℹ️ No configuration files (using defaults)")
|
|
180
|
-
|
|
203
|
+
|
|
181
204
|
# Environment variables
|
|
182
205
|
env_vars = ["LINEAR_API_KEY", "GITHUB_TOKEN", "JIRA_SERVER"]
|
|
183
206
|
env_count = sum(1 for var in env_vars if os.getenv(var))
|
|
184
|
-
|
|
207
|
+
|
|
185
208
|
if env_count > 0:
|
|
186
209
|
console.print(f"✅ {env_count} adapter environment variables configured")
|
|
187
210
|
else:
|
|
188
211
|
console.print("ℹ️ No adapter environment variables (using aitrackdown)")
|
|
189
|
-
|
|
212
|
+
|
|
190
213
|
# Recommendations
|
|
191
|
-
if not
|
|
192
|
-
|
|
214
|
+
if not issues:
|
|
215
|
+
recommendations.append("✅ System appears healthy")
|
|
193
216
|
else:
|
|
194
|
-
|
|
195
|
-
|
|
217
|
+
recommendations.append("🚨 Critical issues detected - see above")
|
|
218
|
+
|
|
196
219
|
if not config_found and env_count == 0:
|
|
197
|
-
|
|
198
|
-
|
|
220
|
+
recommendations.append("💡 Consider running: mcp-ticketer init-aitrackdown")
|
|
221
|
+
|
|
199
222
|
# Display summary
|
|
200
223
|
console.print("\n" + "=" * 60)
|
|
201
224
|
console.print("📋 [bold green]DIAGNOSIS SUMMARY[/bold green]")
|
|
202
225
|
console.print("=" * 60)
|
|
203
|
-
|
|
226
|
+
|
|
204
227
|
if report["issues"]:
|
|
205
228
|
console.print(f"\n🚨 [bold red]Issues ({len(report['issues'])}):[/bold red]")
|
|
206
229
|
for issue in report["issues"]:
|
|
207
230
|
console.print(f" • {issue}")
|
|
208
|
-
|
|
231
|
+
|
|
209
232
|
if report["warnings"]:
|
|
210
|
-
console.print(
|
|
233
|
+
console.print(
|
|
234
|
+
f"\n⚠️ [bold yellow]Warnings ({len(report['warnings'])}):[/bold yellow]"
|
|
235
|
+
)
|
|
211
236
|
for warning in report["warnings"]:
|
|
212
237
|
console.print(f" • {warning}")
|
|
213
|
-
|
|
238
|
+
|
|
214
239
|
if report["recommendations"]:
|
|
215
|
-
console.print(
|
|
240
|
+
console.print("\n💡 [bold blue]Recommendations:[/bold blue]")
|
|
216
241
|
for rec in report["recommendations"]:
|
|
217
242
|
console.print(f" {rec}")
|
|
218
|
-
|
|
243
|
+
|
|
219
244
|
return report
|