This commit is contained in:
2026-01-09 16:36:56 -08:00
parent 6c13604664
commit 0b7832c03c
2 changed files with 384 additions and 13 deletions

View File

@@ -43,6 +43,9 @@ Optional flags:
--no-deno Skip installing the Deno runtime
--deno-version Pin a specific Deno version to install (e.g., v1.34.3)
--upgrade-pip Upgrade pip, setuptools, and wheel before installing deps
--check-install Verify that the 'mm' command was installed correctly
--debug Show detailed diagnostic information during installation
--quiet Suppress output (used internally by platform scripts)
"""
from __future__ import annotations
@@ -289,6 +292,16 @@ def main() -> int:
action="store_true",
help="Upgrade pip/setuptools/wheel before installing requirements",
)
parser.add_argument(
"--debug",
action="store_true",
help="Show detailed diagnostic information during installation",
)
parser.add_argument(
"--check-install",
action="store_true",
help="Verify that the 'mm' command was installed correctly",
)
parser.add_argument(
"--uninstall",
action="store_true",
@@ -528,10 +541,140 @@ def main() -> int:
if args.uninstall:
return _do_uninstall()
# If invoked without any arguments and not asked to skip delegation, prefer
# the interactive menu when running in a TTY; otherwise delegate to the
# platform-specific bootstrap helper (non-interactive).
if len(sys.argv) == 1 and not args.no_delegate:
if args.check_install:
# Verify mm command is properly installed
home = Path.home()
system = platform.system().lower()
print("Checking 'mm' command installation...")
print()
if system == "windows":
user_bin = Path(os.environ.get("USERPROFILE", str(home))) / "bin"
mm_ps1 = user_bin / "mm.ps1"
mm_bat = user_bin / "mm.bat"
print(f"Checking for shim files:")
print(f" mm.ps1: {'' if mm_ps1.exists() else ''} ({mm_ps1})")
print(f" mm.bat: {'' if mm_bat.exists() else ''} ({mm_bat})")
print()
if mm_ps1.exists():
ps1_content = mm_ps1.read_text(encoding="utf-8")
if "$repo" in ps1_content or "$REPO" in ps1_content:
print(f" mm.ps1 content looks valid ({len(ps1_content)} bytes)")
else:
print(f" ⚠️ mm.ps1 content may be corrupted")
if mm_bat.exists():
bat_content = mm_bat.read_text(encoding="utf-8")
if "REPO=" in bat_content or "PY=" in bat_content:
print(f" mm.bat content looks valid ({len(bat_content)} bytes)")
else:
print(f" ⚠️ mm.bat content may be corrupted")
print()
# Check PATH
path = os.environ.get("PATH", "")
user_bin_str = str(user_bin)
in_path = user_bin_str in path
print(f"Checking PATH environment variable:")
print(f" {user_bin_str} in current session PATH: {'' if in_path else ''}")
# Check registry
try:
import winreg
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
key = winreg.OpenKey(reg, "Environment", 0, winreg.KEY_READ)
current_path = winreg.QueryValueEx(key, "Path")[0]
winreg.CloseKey(key)
in_reg = user_bin_str in current_path
print(f" {user_bin_str} in registry PATH: {'' if in_reg else ''}")
if not in_reg:
print()
print("📝 Note: Path is not in registry. It may work in this session but won't persist.")
print(f" To fix, run: [Environment]::SetEnvironmentVariable('PATH', '{user_bin_str};' + [Environment]::GetEnvironmentVariable('PATH','User'), 'User')")
except Exception as e:
print(f" Could not check registry: {e}")
print()
# Test if mm command works
print("Testing 'mm' command...")
try:
result = subprocess.run(["mm", "--help"], capture_output=True, text=True, timeout=5)
if result.returncode == 0:
print(f"'mm --help' works!")
print(f" Output (first line): {result.stdout.split(chr(10))[0]}")
else:
print(f"'mm --help' failed with exit code {result.returncode}")
if result.stderr:
print(f" Error: {result.stderr.strip()}")
except FileNotFoundError:
# mm not found via PATH, try calling the .ps1 directly
print(f"'mm' command not found in PATH")
print(f" Shims exist but command is not accessible via PATH")
print()
print("Attempting to call shim directly...")
try:
result = subprocess.run(
["powershell", "-NoProfile", "-Command", f"& '{mm_ps1}' --help"],
capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
print(f" ✓ Direct shim call works!")
print(f" The shim files are valid and functional.")
print()
print("⚠️ 'mm' is not in PATH, but the shims are working correctly.")
print()
print("Possible causes and fixes:")
print(f" 1. Terminal needs restart: Close and reopen your terminal/PowerShell")
print(f" 2. PATH reload: Run: $env:Path = [Environment]::GetEnvironmentVariable('PATH', 'User') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'Machine')")
print(f" 3. Manual PATH: Add C:\\Users\\Admin\\bin to your system PATH manually")
else:
print(f" ✗ Direct shim call failed")
if result.stderr:
print(f" Error: {result.stderr.strip()}")
except Exception as e:
print(f" ✗ Could not test direct shim: {e}")
except subprocess.TimeoutExpired:
print(f"'mm' command timed out")
except Exception as e:
print(f" ✗ Error testing 'mm': {e}")
else:
# POSIX (Linux/macOS)
user_bin = home / ".local" / "bin"
mm_sh = user_bin / "mm"
print(f"Checking for shim file:")
print(f" mm: {'' if mm_sh.exists() else ''} ({mm_sh})")
print()
path = os.environ.get("PATH", "")
user_bin_str = str(user_bin)
in_path = user_bin_str in path
print(f"Checking PATH environment variable:")
print(f" {user_bin_str} in current session PATH: {'' if in_path else ''}")
print()
# Test if mm command works
print("Testing 'mm' command...")
try:
result = subprocess.run(["mm", "--help"], capture_output=True, text=True, timeout=5)
if result.returncode == 0:
print(f"'mm --help' works!")
print(f" Output (first line): {result.stdout.split(chr(10))[0]}")
else:
print(f"'mm --help' failed with exit code {result.returncode}")
if result.stderr:
print(f" Error: {result.stderr.strip()}")
except FileNotFoundError:
print(f"'mm' command not found in PATH")
except Exception as e:
print(f" ✗ Error testing 'mm': {e}")
print()
print("✅ Installation check complete!")
return 0
if sys.stdin.isatty() and not args.quiet:
sel = _interactive_menu()
if sel == "install":
@@ -852,6 +995,12 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
user_bin = Path(os.environ.get("USERPROFILE", str(home))) / "bin"
user_bin.mkdir(parents=True, exist_ok=True)
# Validate repo path
if not (repo / ".venv").exists():
print(f"WARNING: venv not found at {repo}/.venv - mm command may not work", file=sys.stderr)
if not (repo / "scripts").exists():
print(f"WARNING: scripts folder not found at {repo}/scripts - mm command may not work", file=sys.stderr)
# Convert repo path to string with proper escaping
repo_str = str(repo).replace("\\", "\\\\")
@@ -877,6 +1026,8 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
bak = mm_ps1.with_suffix(f".bak{int(time.time())}")
mm_ps1.replace(bak)
mm_ps1.write_text(ps1_text, encoding="utf-8")
if not args.quiet:
print(f"Created {mm_ps1}")
# Write mm.bat (CMD shim for better compatibility)
mm_bat = user_bin / "mm.bat"
@@ -905,6 +1056,20 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
mm_bat.replace(bak)
mm_bat.write_text(bat_text, encoding="utf-8")
# Validate that shim files were created correctly
ps1_ok = mm_ps1.exists() and len(mm_ps1.read_text(encoding="utf-8")) > 0
bat_ok = mm_bat.exists() and len(mm_bat.read_text(encoding="utf-8")) > 0
if not ps1_ok or not bat_ok:
raise RuntimeError(f"Failed to create shim files: mm.ps1={ps1_ok}, mm.bat={bat_ok}")
if args.debug:
print(f"DEBUG: Created mm.ps1 ({len(ps1_text)} bytes)")
print(f"DEBUG: Created mm.bat ({len(bat_text)} bytes)")
print(f"DEBUG: Repo path embedded in shims: {repo}")
print(f"DEBUG: Venv location: {repo}/.venv")
print(f"DEBUG: Shim directory: {user_bin}")
# Add user_bin to PATH for current and future sessions
str_bin = str(user_bin)
cur_path = os.environ.get("PATH", "")
@@ -912,8 +1077,6 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
# Update current session PATH if not already present
if str_bin not in cur_path:
os.environ["PATH"] = str_bin + ";" + cur_path
if not args.quiet:
print(f"Added {user_bin} to current session PATH")
# Persist to user's Windows registry PATH for future sessions
try:
@@ -922,21 +1085,51 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
"$cur = [Environment]::GetEnvironmentVariable('PATH','User');"
"if ($cur -notlike \"*$bin*\") {{"
"[Environment]::SetEnvironmentVariable('PATH', ($bin + ';' + ($cur -ne $null ? $cur : '')), 'User');"
"Write-Host 'Added {bin} to User PATH in registry' -ForegroundColor Green"
"}}"
).format(bin=str_bin.replace("\\", "\\\\"))
subprocess.run(
result = subprocess.run(
["powershell",
"-NoProfile",
"-Command",
ps_cmd],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
capture_output=True,
text=True
)
if args.debug and result.stderr:
print(f"DEBUG: PowerShell output: {result.stderr}")
# Also reload PATH in current session for immediate availability
reload_cmd = (
"$env:PATH = [Environment]::GetEnvironmentVariable('PATH', 'User') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'Machine')"
)
subprocess.run(
["powershell",
"-NoProfile",
"-Command",
reload_cmd],
check=False,
capture_output=True,
text=True
)
except Exception as e:
if not args.quiet:
print(f"Note: Could not persist PATH to registry (this is non-critical): {e}", file=sys.stderr)
if args.debug:
print(f"DEBUG: Could not persist PATH to registry: {e}", file=sys.stderr)
if not args.quiet:
print(f"Installed global launchers to: {user_bin}")
print(f"✓ mm.ps1 (PowerShell)")
print(f"✓ mm.bat (Command Prompt)")
print()
print("You can now run 'mm' from any shell to start the application.")
print()
print("📝 If 'mm' is not recognized, you may need to:")
print(f" 1. Restart your terminal")
print(f" 2. Or add '{user_bin}' manually to your PATH")
print()
print("🐛 Debug mode: Set MM_DEBUG=1 environment variable for detailed info:")
print(" PowerShell: $env:MM_DEBUG=1; mm --help")
print(" CMD: set MM_DEBUG=1 && mm --help")
if not args.quiet:
print(f"\nInstalled global launchers to: {user_bin}")
@@ -1052,7 +1245,18 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
_install_user_shims(repo_root)
print("Setup complete.")
if not args.quiet:
print("\n✅ Setup complete!")
print()
print("The 'mm' command should now work from any terminal.")
print()
print("Verify the installation:")
print(" python scripts/bootstrap.py --check-install")
print()
print("Then run the app:")
print(" mm --help")
print()
print("💡 If 'mm' is not recognized, close and reopen your terminal.")
return 0
except subprocess.CalledProcessError as exc: