Files
ClaudeProjectManager-main/terminal_launcher.py
Claude Project Manager ec92da8a64 Initial commit
2025-07-07 22:11:38 +02:00

264 Zeilen
10 KiB
Python

"""
Terminal Launcher Module
Handles launching Claude in WSL terminal with specified directory
"""
import subprocess
import os
import tempfile
from datetime import datetime
from typing import Optional, Union
from utils.logger import logger
class TerminalLauncher:
def __init__(self):
self.claude_path = self.find_claude_path()
def find_claude_path(self) -> str:
"""Find Claude path dynamically in WSL"""
# Try to find claude using 'which' command in WSL with login shell
try:
# Hide console window on Windows
startupinfo = None
if hasattr(subprocess, 'STARTUPINFO'):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# Use login shell to ensure all PATH and aliases are loaded
command = ["wsl", "bash", "-l", "-c", "which claude 2>/dev/null || command -v claude 2>/dev/null || type -p claude 2>/dev/null"]
logger.debug(f"Executing command to find Claude: {' '.join(command)}")
result = subprocess.run(
command,
capture_output=True,
text=True,
timeout=5,
startupinfo=startupinfo
)
if result.returncode == 0 and result.stdout.strip():
claude_path = result.stdout.strip()
# Clean up any extra output from 'type' command
if " is " in claude_path:
claude_path = claude_path.split(" is ")[-1].strip()
logger.info(f"Found Claude at: {claude_path}")
print(f"Found Claude at: {claude_path}")
return claude_path
except Exception as e:
logger.error(f"Error finding Claude path: {e}")
print(f"Error finding Claude path: {e}")
# Fallback paths to try
fallback_paths = [
"claude", # Try just 'claude' - let WSL PATH handle it
"/usr/local/bin/claude",
"/usr/bin/claude",
"~/.local/bin/claude",
"/home/$USER/.nvm/versions/node/*/bin/claude" # Wildcard for any node version
]
# Test each fallback path
for path in fallback_paths:
try:
# Hide console window on Windows
startupinfo = None
if hasattr(subprocess, 'STARTUPINFO'):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
result = subprocess.run(
["wsl", "bash", "-c", f"test -f {path} && echo 'exists'"],
capture_output=True,
text=True,
timeout=2,
startupinfo=startupinfo
)
if result.stdout.strip() == 'exists':
print(f"Found Claude at fallback: {path}")
return path
except:
continue
# If nothing found, default to 'claude' and hope it's in PATH
print("Claude path not found, using 'claude' (hoping it's in PATH)")
return "claude"
def convert_to_wsl_path(self, windows_path: str) -> str:
"""Convert Windows path to WSL format"""
# Handle different path formats
windows_path = windows_path.replace('/', '\\')
# Extract drive letter and path
if ':' in windows_path:
drive = windows_path[0].lower()
path = windows_path[2:].replace('\\', '/')
return f'/mnt/{drive}{path}'
return windows_path
def launch_claude_wsl(self, project_path: str, project_name: str = "Claude Session") -> Union[subprocess.Popen, None]:
"""Launch Claude in WSL terminal for specified directory"""
try:
wsl_path = self.convert_to_wsl_path(project_path)
logger.info(f"Launching Claude for project: {project_name} at path: {project_path}")
# Check if Windows Terminal is available
if os.path.exists(r"C:\Windows\System32\wt.exe"):
# Use Windows Terminal
cmd = [
"wt.exe",
"-w", "0",
"new-tab",
"--title", f"Claude - {project_name}",
"--suppressApplicationTitle",
"--",
"wsl", "bash", "-l", "-i", "-c",
f"cd '{wsl_path}' && echo 'Working directory: {wsl_path}' && echo '' && claude || (echo 'Error: Claude not found. Please install Claude in WSL.' && exec bash)"
]
logger.info(f"Executing Windows Terminal command: {' '.join(cmd)}")
process = subprocess.Popen(cmd, shell=False)
logger.info(f"Successfully launched Claude in Windows Terminal for: {project_path}")
print(f"Launched Claude in Windows Terminal for: {project_path}")
return process
else:
# Fallback to cmd window
batch_content = f'''@echo off
title Claude - {project_name}
echo Starting Claude for project: {project_name}
echo Working directory: {project_path}
echo.
wsl bash -l -i -c "cd '{wsl_path}' && claude || (echo 'Error: Claude not found. Please install Claude in WSL.' && exec bash)"
pause
'''
# Create temporary batch file
with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
f.write(batch_content)
batch_path = f.name
# Launch in new cmd window
cmd = ['cmd', '/c', 'start', '', batch_path]
logger.info(f"Executing CMD command: {' '.join(cmd)}")
logger.debug(f"Batch file content:\n{batch_content}")
process = subprocess.Popen(cmd, shell=False)
logger.info(f"Successfully launched Claude in CMD for: {project_path}")
print(f"Launched Claude in CMD for: {project_path}")
return process
except Exception as e:
logger.error(f"Error launching Claude: {str(e)}", extra={"project_path": project_path, "project_name": project_name})
print(f"Error launching Claude: {str(e)}")
return None
def launch_claude_vps(self, server: str, username: str, password: str) -> bool:
"""Launch Claude on VPS via SSH"""
try:
# Create SSH command with password
ssh_command = f'ssh {username}@{server}'
# Create batch file for SSH connection
batch_content = f'''@echo off
title Claude - VPS Server
echo Connecting to VPS Server...
echo Server: {server}
echo Username: {username}
echo.
echo Please enter password when prompted: {password}
echo.
echo After connection, type 'claude' to start Claude
echo.
{ssh_command}
pause
'''
# Create temporary batch file
with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
f.write(batch_content)
batch_path = f.name
# Launch in new window
if os.path.exists(r"C:\Windows\System32\wt.exe"):
# Windows Terminal
cmd = [
"wt.exe",
"-w", "0",
"new-tab",
"--title", "Claude - VPS",
"--",
"cmd", "/c", batch_path
]
subprocess.Popen(cmd)
else:
# CMD fallback
subprocess.Popen(['cmd', '/c', 'start', '', batch_path])
print(f"Launched VPS connection to: {server}")
return True
except Exception as e:
print(f"Error launching VPS connection: {str(e)}")
return False
def launch_claude_admin_panel(self, project_name: str = "Admin Panel") -> Union[subprocess.Popen, None]:
"""Launch Claude for Admin Panel with directory change to /opt/v2-Docker"""
try:
# Check if Windows Terminal is available
if os.path.exists(r"C:\Windows\System32\wt.exe"):
# Use Windows Terminal
cmd = [
"wt.exe",
"-w", "0",
"new-tab",
"--title", f"Claude - {project_name}",
"--suppressApplicationTitle",
"--",
"wsl", "bash", "-l", "-i", "-c",
f"cd /opt/v2-Docker && echo 'Working directory: /opt/v2-Docker' && echo '' && claude || (echo 'Error: Claude not found. Please install Claude in WSL.' && exec bash)"
]
process = subprocess.Popen(cmd, shell=False)
print(f"Launched Claude in Windows Terminal for Admin Panel")
return process
else:
# Fallback to cmd window
batch_content = f'''@echo off
title Claude - {project_name}
echo Starting Claude for: {project_name}
echo Working directory: /opt/v2-Docker
echo.
wsl bash -l -i -c "cd /opt/v2-Docker && claude || (echo 'Error: Claude not found. Please install Claude in WSL.' && exec bash)"
pause
'''
# Create temporary batch file
with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
f.write(batch_content)
batch_path = f.name
# Launch in new cmd window
process = subprocess.Popen(['cmd', '/c', 'start', '', batch_path], shell=False)
print(f"Launched Claude in CMD for Admin Panel")
return process
except Exception as e:
print(f"Error launching Claude for Admin Panel: {str(e)}")
return None
# Test functions
if __name__ == "__main__":
launcher = TerminalLauncher()
# Test WSL path conversion
test_paths = [
"C:\\Users\\hendr\\Desktop\\test",
"D:/Projects/MyApp",
"C:\\Program Files\\test"
]
print("Testing WSL path conversion:")
for path in test_paths:
wsl_path = launcher.convert_to_wsl_path(path)
print(f"{path} -> {wsl_path}")