quash-mcp 0.2.0__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 quash-mcp might be problematic. Click here for more details.

@@ -0,0 +1,739 @@
1
+ """
2
+ Build tool V2 - Comprehensive dependency checker and installer.
3
+ Checks, installs, validates versions, and fixes PATH issues.
4
+ """
5
+
6
+ import sys
7
+ import subprocess
8
+ import shutil
9
+ import platform
10
+ import os
11
+ from typing import Dict, Any, Tuple, List
12
+ from pathlib import Path
13
+
14
+
15
+ class DependencyChecker:
16
+ """Comprehensive dependency checker and installer"""
17
+
18
+ def __init__(self):
19
+ self.os_type = platform.system() # Darwin, Linux, Windows
20
+ self.issues = []
21
+ self.successes = []
22
+
23
+ # ==================== PYTHON VERSION ====================
24
+
25
+ def check_python_version(self) -> Tuple[bool, str, str]:
26
+ """Check if Python version is >= 3.11 and show path"""
27
+ version = sys.version_info
28
+ current = f"{version.major}.{version.minor}.{version.micro}"
29
+ python_path = sys.executable
30
+
31
+ if version.major >= 3 and version.minor >= 11:
32
+ return True, f"✓ Python {current} at {python_path}", None
33
+ else:
34
+ return False, f"✗ Python {current} at {python_path} (requires >= 3.11)", \
35
+ f"Please upgrade Python to 3.11 or higher. Current: {current}"
36
+
37
+ def install_python(self) -> Tuple[bool, str]:
38
+ """Attempt to install Python 3.13 based on OS"""
39
+
40
+ if self.os_type == "Darwin": # macOS
41
+ return self._install_python_macos()
42
+ elif self.os_type == "Linux":
43
+ return self._install_python_linux()
44
+ elif self.os_type == "Windows":
45
+ return self._install_python_windows()
46
+ else:
47
+ return False, f"✗ Unsupported OS: {self.os_type}"
48
+
49
+ def _install_python_macos(self) -> Tuple[bool, str]:
50
+ """Install Python 3.13 on macOS via Homebrew"""
51
+ if not shutil.which("brew"):
52
+ return False, "✗ Homebrew not found. Install from: https://brew.sh/"
53
+
54
+ try:
55
+ print(" Installing Python 3.13 via Homebrew...")
56
+ subprocess.run(
57
+ ["brew", "install", "python@3.13"],
58
+ check=True,
59
+ capture_output=True,
60
+ timeout=600,
61
+ text=True
62
+ )
63
+ return True, "✓ Python 3.13 installed via Homebrew. Please restart your terminal and run build again."
64
+ except subprocess.TimeoutExpired:
65
+ return False, "✗ Installation timed out"
66
+ except subprocess.CalledProcessError as e:
67
+ error_msg = e.stderr if e.stderr else str(e)
68
+ return False, f"✗ Homebrew install failed: {error_msg}"
69
+ except Exception as e:
70
+ return False, f"✗ Installation error: {str(e)}"
71
+
72
+ def _install_python_linux(self) -> Tuple[bool, str]:
73
+ """Install Python 3.13 on Linux"""
74
+ # Try deadsnakes PPA for Ubuntu/Debian
75
+ if shutil.which("apt-get"):
76
+ try:
77
+ print(" Adding deadsnakes PPA and installing Python 3.13...")
78
+ subprocess.run(
79
+ ["sudo", "apt-get", "install", "-y", "software-properties-common"],
80
+ check=True,
81
+ capture_output=True,
82
+ timeout=120
83
+ )
84
+ subprocess.run(
85
+ ["sudo", "add-apt-repository", "-y", "ppa:deadsnakes/ppa"],
86
+ check=True,
87
+ capture_output=True,
88
+ timeout=120
89
+ )
90
+ subprocess.run(
91
+ ["sudo", "apt-get", "update"],
92
+ check=True,
93
+ capture_output=True,
94
+ timeout=120
95
+ )
96
+ subprocess.run(
97
+ ["sudo", "apt-get", "install", "-y", "python3.13", "python3.13-venv"],
98
+ check=True,
99
+ capture_output=True,
100
+ timeout=300
101
+ )
102
+ return True, "✓ Python 3.13 installed. Use 'python3.13' to run."
103
+ except Exception as e:
104
+ return False, f"✗ apt-get install failed: {str(e)}"
105
+
106
+ # Try dnf for Fedora/RHEL
107
+ elif shutil.which("dnf"):
108
+ try:
109
+ print(" Installing Python 3.13 via dnf...")
110
+ subprocess.run(
111
+ ["sudo", "dnf", "install", "-y", "python3.13"],
112
+ check=True,
113
+ capture_output=True,
114
+ timeout=300
115
+ )
116
+ return True, "✓ Python 3.13 installed via dnf"
117
+ except Exception as e:
118
+ return False, f"✗ dnf install failed: {str(e)}"
119
+ else:
120
+ return False, "✗ No supported package manager found"
121
+
122
+ def _install_python_windows(self) -> Tuple[bool, str]:
123
+ """Install Python on Windows"""
124
+ return False, (
125
+ "✗ Please install Python manually:\n"
126
+ " 1. Download: https://www.python.org/downloads/\n"
127
+ " 2. Run installer and check 'Add to PATH'\n"
128
+ " 3. Install Python 3.11 or higher"
129
+ )
130
+
131
+ # ==================== ADB ====================
132
+
133
+ def check_adb(self) -> Tuple[bool, str, str]:
134
+ """Check if ADB is installed and get version"""
135
+ adb_path = shutil.which("adb")
136
+
137
+ if adb_path:
138
+ try:
139
+ result = subprocess.run(
140
+ ["adb", "version"],
141
+ capture_output=True,
142
+ text=True,
143
+ timeout=5
144
+ )
145
+ version_line = result.stdout.split('\n')[0]
146
+ return True, f"✓ ADB installed at {adb_path} ({version_line})", None
147
+ except Exception as e:
148
+ return True, f"✓ ADB found at {adb_path} (version check failed)", None
149
+
150
+ return False, "✗ ADB not found in PATH", None
151
+
152
+ def install_adb(self) -> Tuple[bool, str]:
153
+ """Attempt to install ADB based on OS"""
154
+
155
+ if self.os_type == "Darwin": # macOS
156
+ return self._install_adb_macos()
157
+ elif self.os_type == "Linux":
158
+ return self._install_adb_linux()
159
+ elif self.os_type == "Windows":
160
+ return self._install_adb_windows()
161
+ else:
162
+ return False, f"✗ Unsupported OS: {self.os_type}"
163
+
164
+ def _install_adb_macos(self) -> Tuple[bool, str]:
165
+ """Install ADB on macOS"""
166
+ # Check if Homebrew is installed
167
+ if not shutil.which("brew"):
168
+ return False, "✗ Homebrew not found. Install from: https://brew.sh/"
169
+
170
+ try:
171
+ print(" Installing ADB via Homebrew...")
172
+ result = subprocess.run(
173
+ ["brew", "install", "android-platform-tools"],
174
+ capture_output=True,
175
+ timeout=300,
176
+ text=True
177
+ )
178
+
179
+ # Check if already installed (cask returns different messages)
180
+ combined_output = (result.stdout + result.stderr).lower()
181
+ already_installed = (
182
+ "android-platform-tools" in combined_output and
183
+ ("already installed" in combined_output or
184
+ "not upgrading" in combined_output or
185
+ "reinstalling" in combined_output)
186
+ )
187
+
188
+ # Verify ADB is in PATH after installation
189
+ if not shutil.which("adb"):
190
+ if already_installed:
191
+ print(" ADB package already installed but not in PATH, reinstalling...")
192
+ # For casks, we need to reinstall to recreate symlinks
193
+ try:
194
+ subprocess.run(
195
+ ["brew", "reinstall", "android-platform-tools"],
196
+ check=True,
197
+ capture_output=True,
198
+ timeout=120
199
+ )
200
+ except Exception as reinstall_error:
201
+ pass # Continue to manual symlink creation
202
+
203
+ # Always check and create symlink manually after reinstall
204
+ # (casks don't automatically create symlinks in /opt/homebrew/bin)
205
+ if not shutil.which("adb"):
206
+ print(" Reinstall completed, creating symlink manually...")
207
+ cask_path = Path("/opt/homebrew/Caskroom/android-platform-tools")
208
+ if cask_path.exists():
209
+ # Find the version directory (sorted to get latest)
210
+ versions = sorted(cask_path.glob("*"))
211
+ if versions:
212
+ adb_src = versions[-1] / "platform-tools" / "adb"
213
+ if adb_src.exists():
214
+ adb_link = Path("/opt/homebrew/bin/adb")
215
+ try:
216
+ if adb_link.exists() or adb_link.is_symlink():
217
+ adb_link.unlink()
218
+ adb_link.symlink_to(adb_src)
219
+ print(f" Created symlink: {adb_link} -> {adb_src}")
220
+ except Exception as symlink_error:
221
+ return False, f"✗ Failed to create symlink: {str(symlink_error)}"
222
+ else:
223
+ return False, f"✗ ADB binary not found at {adb_src}"
224
+ else:
225
+ return False, f"✗ No version directories found in {cask_path}"
226
+ else:
227
+ return False, f"✗ Caskroom path not found: {cask_path}"
228
+ else:
229
+ print(" Installation completed but ADB not found in PATH")
230
+
231
+ # Final verification
232
+ if shutil.which("adb"):
233
+ return True, "✓ ADB installed successfully via Homebrew"
234
+ else:
235
+ return False, "✗ ADB installation completed but not found in PATH. Try restarting terminal."
236
+
237
+ except subprocess.TimeoutExpired:
238
+ return False, "✗ Installation timed out"
239
+ except subprocess.CalledProcessError as e:
240
+ error_msg = e.stderr.decode() if e.stderr else str(e)
241
+ # Even if brew install fails due to already installed, continue to verification
242
+ if "already installed" in error_msg.lower():
243
+ if shutil.which("adb"):
244
+ return True, "✓ ADB already installed via Homebrew"
245
+ return False, f"✗ Homebrew install failed: {error_msg}"
246
+ except Exception as e:
247
+ return False, f"✗ Installation error: {str(e)}"
248
+
249
+ def _install_adb_linux(self) -> Tuple[bool, str]:
250
+ """Install ADB on Linux"""
251
+ # Try apt-get first (Debian/Ubuntu)
252
+ if shutil.which("apt-get"):
253
+ try:
254
+ print(" Installing ADB via apt-get (may require sudo)...")
255
+ subprocess.run(
256
+ ["sudo", "apt-get", "update"],
257
+ check=True,
258
+ capture_output=True,
259
+ timeout=120
260
+ )
261
+ subprocess.run(
262
+ ["sudo", "apt-get", "install", "-y", "adb"],
263
+ check=True,
264
+ capture_output=True,
265
+ timeout=300
266
+ )
267
+ return True, "✓ ADB installed successfully via apt-get"
268
+ except Exception as e:
269
+ return False, f"✗ apt-get install failed: {str(e)}"
270
+
271
+ # Try dnf (Fedora/RHEL)
272
+ elif shutil.which("dnf"):
273
+ try:
274
+ print(" Installing ADB via dnf (may require sudo)...")
275
+ subprocess.run(
276
+ ["sudo", "dnf", "install", "-y", "android-tools"],
277
+ check=True,
278
+ capture_output=True,
279
+ timeout=300
280
+ )
281
+ return True, "✓ ADB installed successfully via dnf"
282
+ except Exception as e:
283
+ return False, f"✗ dnf install failed: {str(e)}"
284
+
285
+ else:
286
+ return False, "✗ No supported package manager found (apt-get/dnf)"
287
+
288
+ def _install_adb_windows(self) -> Tuple[bool, str]:
289
+ """Install ADB on Windows"""
290
+ # Windows doesn't have good package manager by default
291
+ # Provide download link instead
292
+ return False, (
293
+ "✗ Please install ADB manually:\n"
294
+ " 1. Download: https://developer.android.com/tools/releases/platform-tools\n"
295
+ " 2. Extract to C:\\platform-tools\n"
296
+ " 3. Add to PATH: C:\\platform-tools"
297
+ )
298
+
299
+ def check_adb_in_path(self) -> Tuple[bool, str, str]:
300
+ """Check if ADB is in PATH and suggest fix if not"""
301
+ adb_path = shutil.which("adb")
302
+
303
+ if adb_path:
304
+ return True, f"✓ ADB in PATH: {adb_path}", None
305
+
306
+ # Try to find ADB in common locations
307
+ common_paths = []
308
+
309
+ if self.os_type == "Darwin":
310
+ common_paths = [
311
+ "/opt/homebrew/bin/adb",
312
+ "/usr/local/bin/adb",
313
+ Path.home() / "Library/Android/sdk/platform-tools/adb",
314
+ ]
315
+ elif self.os_type == "Linux":
316
+ common_paths = [
317
+ "/usr/bin/adb",
318
+ "/usr/local/bin/adb",
319
+ Path.home() / "Android/Sdk/platform-tools/adb",
320
+ ]
321
+ elif self.os_type == "Windows":
322
+ common_paths = [
323
+ "C:\\platform-tools\\adb.exe",
324
+ "C:\\Android\\sdk\\platform-tools\\adb.exe",
325
+ Path.home() / "AppData/Local/Android/Sdk/platform-tools/adb.exe",
326
+ ]
327
+
328
+ for path in common_paths:
329
+ if Path(path).exists():
330
+ return False, f"⚠️ ADB found at {path} but not in PATH", str(path)
331
+
332
+ return False, "✗ ADB not found anywhere", None
333
+
334
+ def add_to_path(self, adb_path: str) -> Tuple[bool, str]:
335
+ """Add ADB directory to PATH"""
336
+ adb_dir = str(Path(adb_path).parent)
337
+
338
+ shell_rc = None
339
+ if self.os_type == "Darwin":
340
+ # Check which shell
341
+ shell = os.environ.get("SHELL", "")
342
+ if "zsh" in shell:
343
+ shell_rc = Path.home() / ".zshrc"
344
+ else:
345
+ shell_rc = Path.home() / ".bash_profile"
346
+ elif self.os_type == "Linux":
347
+ shell_rc = Path.home() / ".bashrc"
348
+
349
+ if shell_rc:
350
+ try:
351
+ with open(shell_rc, "a") as f:
352
+ f.write(f'\n# Added by Quash MCP\nexport PATH="$PATH:{adb_dir}"\n')
353
+
354
+ return True, (
355
+ f"✓ Added to PATH in {shell_rc}\n"
356
+ f" Please run: source {shell_rc}\n"
357
+ f" Or restart your terminal"
358
+ )
359
+ except Exception as e:
360
+ return False, f"✗ Failed to update {shell_rc}: {str(e)}"
361
+
362
+ return False, "✗ Could not determine shell config file"
363
+
364
+ # ==================== MAHORAGA PACKAGE ====================
365
+
366
+ def check_mahoraga(self) -> Tuple[bool, str, str]:
367
+ """Check if Quash package is installed"""
368
+ try:
369
+ import mahoraga
370
+ version = getattr(mahoraga, "__version__", "unknown")
371
+ location = Path(mahoraga.__file__).parent
372
+ return True, f"✓ Quash v{version} installed at {location}", None
373
+ except ImportError:
374
+ return False, "✗ Quash package not installed", None
375
+
376
+ def install_mahoraga(self) -> Tuple[bool, str]:
377
+ """Install Quash package"""
378
+ try:
379
+ # Check if we're in development mode (source available)
380
+ project_root = Path(__file__).parent.parent.parent.parent
381
+ mahoraga_src = project_root / "mahoraga"
382
+
383
+ if mahoraga_src.exists() and (mahoraga_src / "pyproject.toml").exists():
384
+ # Install from local source
385
+ print(f" Installing Quash from {mahoraga_src}...")
386
+ subprocess.run(
387
+ [sys.executable, "-m", "pip", "install", "-e", str(mahoraga_src)],
388
+ check=True,
389
+ capture_output=True
390
+ )
391
+ return True, f"✓ Quash installed from local source: {mahoraga_src}"
392
+ else:
393
+ # Try to install from PyPI (if published)
394
+ print(" Installing Quash from PyPI...")
395
+ subprocess.run(
396
+ [sys.executable, "-m", "pip", "install", "mahoraga"],
397
+ check=True,
398
+ capture_output=True
399
+ )
400
+ return True, "✓ Quash installed from PyPI"
401
+
402
+ except subprocess.CalledProcessError as e:
403
+ error_msg = e.stderr.decode() if e.stderr else str(e)
404
+ return False, f"✗ Installation failed: {error_msg}"
405
+ except Exception as e:
406
+ return False, f"✗ Error: {str(e)}"
407
+
408
+ def check_mahoraga_version(self) -> Tuple[bool, str, str]:
409
+ """Check if Quash version is compatible"""
410
+ try:
411
+ import mahoraga
412
+ version = getattr(mahoraga, "__version__", "0.0.0")
413
+
414
+ # Parse version
415
+ major, minor, patch = version.split(".")[:3]
416
+
417
+ # We need at least 0.3.0
418
+ if int(major) == 0 and int(minor) >= 3:
419
+ return True, f"✓ Quash v{version} (compatible)", None
420
+ elif int(major) > 0:
421
+ return True, f"✓ Quash v{version} (compatible)", None
422
+ else:
423
+ return False, f"✗ Quash v{version} (requires >= 0.3.0)", version
424
+ except Exception as e:
425
+ return False, f"✗ Version check failed: {str(e)}", None
426
+
427
+ # ==================== PYTHON DEPENDENCIES ====================
428
+
429
+ def check_python_dependencies(self) -> Tuple[bool, str, List[str]]:
430
+ """Check if all required Python packages are installed with correct versions"""
431
+ required_packages = {
432
+ "click": "8.1.0",
433
+ "rich": "13.0.0",
434
+ "pydantic": "2.0.0",
435
+ "aiofiles": "23.0.0",
436
+ "openai": "1.0.0",
437
+ "pillow": "10.0.0",
438
+ "python-dotenv": "1.0.0",
439
+ "requests": "2.31.0",
440
+ "llama-index": None, # No specific version
441
+ "llama-index-llms-openai-like": None,
442
+ "adbutils": "2.10.0", # Exact version
443
+ "apkutils": "2.0.0", # Exact version
444
+ }
445
+
446
+ missing = []
447
+ incompatible = []
448
+
449
+ for package, min_version in required_packages.items():
450
+ try:
451
+ # Import the package to check if it's installed
452
+ if package == "python-dotenv":
453
+ import dotenv
454
+ pkg = dotenv
455
+ pkg_name = "dotenv"
456
+ elif package == "pillow":
457
+ from PIL import Image
458
+ pkg = Image
459
+ pkg_name = "PIL"
460
+ elif package == "llama-index-llms-openai-like":
461
+ from llama_index.llms import openai_like
462
+ pkg = openai_like
463
+ pkg_name = "llama_index.llms.openai_like"
464
+ else:
465
+ pkg_name = package.replace("-", "_")
466
+ pkg = __import__(pkg_name)
467
+
468
+ # Check version if required
469
+ if min_version:
470
+ version = getattr(pkg, "__version__", None)
471
+ if version:
472
+ # Parse version numbers
473
+ current_parts = version.split(".")[:3]
474
+ required_parts = min_version.split(".")[:3]
475
+
476
+ # For exact version requirements (adbutils, apkutils)
477
+ if package in ["adbutils", "apkutils"]:
478
+ if version != min_version:
479
+ incompatible.append(f"{package}=={min_version} (current: {version})")
480
+ else:
481
+ # For minimum version requirements
482
+ try:
483
+ current_ver = tuple(int(x) for x in current_parts)
484
+ required_ver = tuple(int(x) for x in required_parts)
485
+ if current_ver < required_ver:
486
+ incompatible.append(f"{package}>={min_version} (current: {version})")
487
+ except ValueError:
488
+ # If version parsing fails, skip version check
489
+ pass
490
+ except ImportError:
491
+ missing.append(package)
492
+
493
+ if missing or incompatible:
494
+ msg_parts = []
495
+ if missing:
496
+ msg_parts.append(f"Missing: {', '.join(missing)}")
497
+ if incompatible:
498
+ msg_parts.append(f"Incompatible: {', '.join(incompatible)}")
499
+ return False, f"✗ Python dependencies: {'; '.join(msg_parts)}", missing + incompatible
500
+ else:
501
+ return True, f"✓ All Python dependencies installed", None
502
+
503
+ def install_python_dependencies(self) -> Tuple[bool, str]:
504
+ """Install all required Python dependencies"""
505
+ try:
506
+ print(" Installing Python dependencies...")
507
+
508
+ # Install from Quash's pyproject.toml if available
509
+ project_root = Path(__file__).parent.parent.parent.parent
510
+ mahoraga_src = project_root / "mahoraga"
511
+
512
+ if mahoraga_src.exists() and (mahoraga_src / "pyproject.toml").exists():
513
+ print(f" Installing from {mahoraga_src}...")
514
+ subprocess.run(
515
+ [sys.executable, "-m", "pip", "install", "-e", str(mahoraga_src)],
516
+ check=True,
517
+ capture_output=True,
518
+ timeout=300
519
+ )
520
+ return True, "✓ Python dependencies installed from local source"
521
+ else:
522
+ # Install individual packages with version constraints
523
+ packages = [
524
+ "click>=8.1.0",
525
+ "rich>=13.0.0",
526
+ "pydantic>=2.0.0",
527
+ "aiofiles>=23.0.0",
528
+ "openai>=1.0.0",
529
+ "pillow>=10.0.0",
530
+ "python-dotenv>=1.0.0",
531
+ "requests>=2.31.0",
532
+ "llama-index",
533
+ "llama-index-llms-openai-like",
534
+ "adbutils==2.10.0",
535
+ "apkutils==2.0.0",
536
+ ]
537
+
538
+ print(f" Installing {len(packages)} packages...")
539
+ subprocess.run(
540
+ [sys.executable, "-m", "pip", "install"] + packages,
541
+ check=True,
542
+ capture_output=True,
543
+ timeout=600
544
+ )
545
+ return True, "✓ Python dependencies installed"
546
+
547
+ except subprocess.TimeoutExpired:
548
+ return False, "✗ Dependency installation timed out"
549
+ except subprocess.CalledProcessError as e:
550
+ error_msg = e.stderr.decode() if e.stderr else str(e)
551
+ return False, f"✗ Dependency installation failed: {error_msg}"
552
+ except Exception as e:
553
+ return False, f"✗ Installation error: {str(e)}"
554
+
555
+ # ==================== PORTAL APK ====================
556
+
557
+ def check_portal(self) -> Tuple[bool, str, str]:
558
+ """Check if Portal APK download works"""
559
+ try:
560
+ from mahoraga.portal import download_portal_apk
561
+ return True, "✓ Portal APK download available", None
562
+ except ImportError as e:
563
+ return False, f"✗ Portal module not found: {str(e)}", None
564
+ except Exception as e:
565
+ return False, f"✗ Portal check failed: {str(e)}", None
566
+
567
+
568
+ async def build() -> Dict[str, Any]:
569
+ """
570
+ Comprehensive build check and setup.
571
+
572
+ 1. Checks all dependencies
573
+ 2. Validates versions
574
+ 3. Auto-installs where possible
575
+ 4. Fixes PATH issues
576
+
577
+ Returns:
578
+ Dict with detailed status of all checks and fixes
579
+ """
580
+ checker = DependencyChecker()
581
+ details = {}
582
+ fixes_applied = []
583
+ all_ok = True
584
+
585
+ print("=" * 70)
586
+ print("MAHORAGA MCP - DEPENDENCY CHECK & SETUP")
587
+ print("=" * 70)
588
+ print()
589
+
590
+ # 1. Check Python Version
591
+ print("1️⃣ Checking Python version...")
592
+ py_ok, py_msg, py_fix = checker.check_python_version()
593
+ details["python"] = py_msg
594
+ print(f" {py_msg}")
595
+
596
+ if not py_ok:
597
+ # Try to install Python 3.13
598
+ print(" Attempting to install Python 3.13...")
599
+ install_ok, install_msg = checker.install_python()
600
+ details["python_install"] = install_msg
601
+ print(f" {install_msg}")
602
+
603
+ if not install_ok:
604
+ all_ok = False
605
+ else:
606
+ fixes_applied.append("Installed Python 3.13")
607
+ print(f" ⚠️ Please restart your terminal and run build again with python3.13")
608
+ print()
609
+
610
+ # 2. Check ADB
611
+ print("2️⃣ Checking ADB...")
612
+ adb_ok, adb_msg, _ = checker.check_adb()
613
+ details["adb"] = adb_msg
614
+ print(f" {adb_msg}")
615
+
616
+ if not adb_ok:
617
+ # Try to install
618
+ print(" Attempting to install ADB...")
619
+ install_ok, install_msg = checker.install_adb()
620
+ details["adb_install"] = install_msg
621
+ print(f" {install_msg}")
622
+
623
+ if not install_ok:
624
+ all_ok = False
625
+ else:
626
+ fixes_applied.append("Installed ADB")
627
+
628
+ # 3. Check ADB in PATH
629
+ print("3️⃣ Checking ADB PATH...")
630
+ path_ok, path_msg, found_path = checker.check_adb_in_path()
631
+ details["adb_path"] = path_msg
632
+ print(f" {path_msg}")
633
+
634
+ if not path_ok and found_path:
635
+ # Try to add to PATH
636
+ print(" Attempting to add ADB to PATH...")
637
+ path_fix_ok, path_fix_msg = checker.add_to_path(found_path)
638
+ details["adb_path_fix"] = path_fix_msg
639
+ print(f" {path_fix_msg}")
640
+
641
+ if path_fix_ok:
642
+ fixes_applied.append("Added ADB to PATH")
643
+ elif not path_ok:
644
+ all_ok = False
645
+ print()
646
+
647
+ # 4. Check Quash Package
648
+ print("4️⃣ Checking Quash package...")
649
+ mhg_ok, mhg_msg, _ = checker.check_mahoraga()
650
+ details["mahoraga"] = mhg_msg
651
+ print(f" {mhg_msg}")
652
+
653
+ mahoraga_just_installed = False
654
+ if not mhg_ok:
655
+ # Try to install
656
+ print(" Attempting to install Quash...")
657
+ install_ok, install_msg = checker.install_mahoraga()
658
+ details["mahoraga_install"] = install_msg
659
+ print(f" {install_msg}")
660
+
661
+ if not install_ok:
662
+ all_ok = False
663
+ else:
664
+ fixes_applied.append("Installed Quash")
665
+ mahoraga_just_installed = True # Track that we just installed Quash
666
+ print()
667
+
668
+ # 5. Check Quash Version
669
+ print("5️⃣ Checking Quash version...")
670
+ ver_ok, ver_msg, ver_fix = checker.check_mahoraga_version()
671
+ details["mahoraga_version"] = ver_msg
672
+ print(f" {ver_msg}")
673
+
674
+ if not ver_ok:
675
+ all_ok = False
676
+ if ver_fix:
677
+ fix_msg = f"Please upgrade: pip install --upgrade mahoraga"
678
+ details["mahoraga_version_fix"] = fix_msg
679
+ print(f" 💡 {fix_msg}")
680
+ print()
681
+
682
+ # 6. Check Python Dependencies
683
+ # Note: Only check if Quash wasn't just installed, because 'pip install -e'
684
+ # automatically installs all dependencies from pyproject.toml
685
+ if mahoraga_just_installed:
686
+ print("6️⃣ Checking Python dependencies...")
687
+ print(f" ⏭️ Skipped (dependencies installed with Quash in step 4)")
688
+ details["python_dependencies"] = "⏭️ Skipped (installed with Quash)"
689
+ else:
690
+ print("6️⃣ Checking Python dependencies...")
691
+ deps_ok, deps_msg, missing_deps = checker.check_python_dependencies()
692
+ details["python_dependencies"] = deps_msg
693
+ print(f" {deps_msg}")
694
+
695
+ if not deps_ok:
696
+ # Try to install missing/incompatible dependencies
697
+ print(" Attempting to install Python dependencies...")
698
+ install_ok, install_msg = checker.install_python_dependencies()
699
+ details["python_dependencies_install"] = install_msg
700
+ print(f" {install_msg}")
701
+
702
+ if not install_ok:
703
+ all_ok = False
704
+ else:
705
+ fixes_applied.append("Installed Python dependencies")
706
+ print()
707
+
708
+ # 7. Check Portal
709
+ print("7️⃣ Checking Portal APK...")
710
+ portal_ok, portal_msg, _ = checker.check_portal()
711
+ details["portal"] = portal_msg
712
+ print(f" {portal_msg}")
713
+
714
+ if not portal_ok:
715
+ all_ok = False
716
+ print()
717
+
718
+ # Final Summary
719
+ print("=" * 70)
720
+ if all_ok:
721
+ status = "success"
722
+ message = "✅ All dependencies ready! You can now use Quash."
723
+ if fixes_applied:
724
+ message += f"\n Fixes applied: {', '.join(fixes_applied)}"
725
+ else:
726
+ failed = [k for k, v in details.items() if v.startswith("✗")]
727
+ status = "failed"
728
+ message = f"❌ Setup incomplete. Issues with: {', '.join(failed)}"
729
+ message += "\n Please review the details above and follow the suggestions."
730
+
731
+ print(message)
732
+ print("=" * 70)
733
+
734
+ return {
735
+ "status": status,
736
+ "details": details,
737
+ "fixes_applied": fixes_applied,
738
+ "message": message
739
+ }