mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-01-17 19:47:00 +01:00
Sync from development - prepare for v0.1.5
This commit is contained in:
@@ -1,247 +0,0 @@
|
||||
"""
|
||||
Tuxborn Command
|
||||
|
||||
CLI command for the Tuxborn Automatic Installer.
|
||||
Extracted from the original jackify-cli.py.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# Import the backend services we'll need
|
||||
from jackify.backend.models.modlist import ModlistContext
|
||||
from jackify.shared.colors import COLOR_INFO, COLOR_ERROR, COLOR_RESET
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TuxbornCommand:
|
||||
"""Handler for the tuxborn-auto CLI command."""
|
||||
|
||||
def __init__(self, backend_services, system_info):
|
||||
"""Initialize with backend services.
|
||||
|
||||
Args:
|
||||
backend_services: Dictionary of backend service instances
|
||||
system_info: System information (steamdeck flag, etc.)
|
||||
"""
|
||||
self.backend_services = backend_services
|
||||
self.system_info = system_info
|
||||
|
||||
def add_args(self, parser):
|
||||
"""Add tuxborn-auto arguments to the main parser.
|
||||
|
||||
Args:
|
||||
parser: The main ArgumentParser
|
||||
"""
|
||||
parser.add_argument(
|
||||
"--tuxborn-auto",
|
||||
action="store_true",
|
||||
help="Run the Tuxborn Automatic Installer non-interactively (for GUI integration)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--install-dir",
|
||||
type=str,
|
||||
help="Install directory for Tuxborn (required with --tuxborn-auto)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--download-dir",
|
||||
type=str,
|
||||
help="Downloads directory for Tuxborn (required with --tuxborn-auto)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--modlist-name",
|
||||
type=str,
|
||||
default="Tuxborn",
|
||||
help="Modlist name (optional, defaults to 'Tuxborn')"
|
||||
)
|
||||
|
||||
def execute(self, args) -> int:
|
||||
"""Execute the tuxborn-auto command.
|
||||
|
||||
Args:
|
||||
args: Parsed command-line arguments
|
||||
|
||||
Returns:
|
||||
Exit code (0 for success, 1 for failure)
|
||||
"""
|
||||
logger.info("Starting Tuxborn Automatic Installer (GUI integration mode)")
|
||||
|
||||
try:
|
||||
# Set up logging redirection (copied from original)
|
||||
self._setup_tee_logging()
|
||||
|
||||
# Build context from args
|
||||
context = self._build_context_from_args(args)
|
||||
|
||||
# Validate required fields
|
||||
if not self._validate_context(context):
|
||||
return 1
|
||||
|
||||
# Use legacy implementation for now - will migrate to backend services later
|
||||
result = self._execute_legacy_tuxborn(context)
|
||||
|
||||
logger.info("Finished Tuxborn Automatic Installer")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to run Tuxborn installer: {e}")
|
||||
print(f"{COLOR_ERROR}Tuxborn installation failed: {e}{COLOR_RESET}")
|
||||
return 1
|
||||
finally:
|
||||
# Restore stdout/stderr
|
||||
self._restore_stdout_stderr()
|
||||
|
||||
def _build_context_from_args(self, args) -> dict:
|
||||
"""Build context dictionary from command arguments.
|
||||
|
||||
Args:
|
||||
args: Parsed command-line arguments
|
||||
|
||||
Returns:
|
||||
Context dictionary
|
||||
"""
|
||||
install_dir = getattr(args, 'install_dir', None)
|
||||
download_dir = getattr(args, 'download_dir', None)
|
||||
modlist_name = getattr(args, 'modlist_name', 'Tuxborn')
|
||||
machineid = 'Tuxborn/Tuxborn'
|
||||
|
||||
# Try to get API key from saved config first, then environment variable
|
||||
from jackify.backend.services.api_key_service import APIKeyService
|
||||
api_key_service = APIKeyService()
|
||||
api_key = api_key_service.get_saved_api_key()
|
||||
if not api_key:
|
||||
api_key = os.environ.get('NEXUS_API_KEY')
|
||||
|
||||
resolution = getattr(args, 'resolution', None)
|
||||
mo2_exe_path = getattr(args, 'mo2_exe_path', None)
|
||||
skip_confirmation = True # Always true in GUI mode
|
||||
|
||||
context = {
|
||||
'machineid': machineid,
|
||||
'modlist_name': modlist_name,
|
||||
'install_dir': install_dir,
|
||||
'download_dir': download_dir,
|
||||
'nexus_api_key': api_key,
|
||||
'skip_confirmation': skip_confirmation,
|
||||
'resolution': resolution,
|
||||
'mo2_exe_path': mo2_exe_path,
|
||||
}
|
||||
|
||||
# PATCH: Always set modlist_value and modlist_source for Tuxborn workflow
|
||||
context['modlist_value'] = 'Tuxborn/Tuxborn'
|
||||
context['modlist_source'] = 'identifier'
|
||||
|
||||
return context
|
||||
|
||||
def _validate_context(self, context: dict) -> bool:
|
||||
"""Validate Tuxborn context.
|
||||
|
||||
Args:
|
||||
context: Tuxborn context dictionary
|
||||
|
||||
Returns:
|
||||
True if valid, False otherwise
|
||||
"""
|
||||
required_keys = ['modlist_name', 'install_dir', 'download_dir', 'nexus_api_key']
|
||||
missing = [k for k in required_keys if not context.get(k)]
|
||||
|
||||
if missing:
|
||||
print(f"{COLOR_ERROR}Missing required arguments for --tuxborn-auto.\\n"
|
||||
f"--install-dir, --download-dir, and NEXUS_API_KEY (env, 32+ chars) are required.{COLOR_RESET}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _setup_tee_logging(self):
|
||||
"""Set up TEE logging (copied from original implementation)."""
|
||||
import shutil
|
||||
|
||||
# TEE logging setup & log rotation (copied from original)
|
||||
class TeeStdout:
|
||||
def __init__(self, *files):
|
||||
self.files = files
|
||||
def write(self, data):
|
||||
for f in self.files:
|
||||
f.write(data)
|
||||
f.flush()
|
||||
def flush(self):
|
||||
for f in self.files:
|
||||
f.flush()
|
||||
|
||||
log_dir = Path.home() / "Jackify" / "logs"
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
workflow_log_path = log_dir / "tuxborn_workflow.log"
|
||||
|
||||
# Log rotation: keep last 3 logs, 1KB each (for testing)
|
||||
max_logs = 3
|
||||
max_size = 1024 # 1KB for testing
|
||||
if workflow_log_path.exists() and workflow_log_path.stat().st_size > max_size:
|
||||
for i in range(max_logs, 0, -1):
|
||||
prev = log_dir / f"tuxborn_workflow.log.{i-1}" if i > 1 else workflow_log_path
|
||||
dest = log_dir / f"tuxborn_workflow.log.{i}"
|
||||
if prev.exists():
|
||||
if dest.exists():
|
||||
dest.unlink()
|
||||
prev.rename(dest)
|
||||
|
||||
self.workflow_log = open(workflow_log_path, 'a')
|
||||
self.orig_stdout, self.orig_stderr = sys.stdout, sys.stderr
|
||||
sys.stdout = TeeStdout(sys.stdout, self.workflow_log)
|
||||
sys.stderr = TeeStdout(sys.stderr, self.workflow_log)
|
||||
|
||||
def _restore_stdout_stderr(self):
|
||||
"""Restore original stdout/stderr."""
|
||||
if hasattr(self, 'orig_stdout'):
|
||||
sys.stdout = self.orig_stdout
|
||||
sys.stderr = self.orig_stderr
|
||||
if hasattr(self, 'workflow_log'):
|
||||
self.workflow_log.close()
|
||||
|
||||
def _execute_legacy_tuxborn(self, context: dict) -> int:
|
||||
"""Execute Tuxborn using legacy implementation.
|
||||
|
||||
Args:
|
||||
context: Tuxborn context dictionary
|
||||
|
||||
Returns:
|
||||
Exit code
|
||||
"""
|
||||
# Import backend services
|
||||
from jackify.backend.core.modlist_operations import ModlistInstallCLI
|
||||
from jackify.backend.handlers.menu_handler import MenuHandler
|
||||
|
||||
# Create legacy handler instances
|
||||
menu_handler = MenuHandler()
|
||||
modlist_cli = ModlistInstallCLI(
|
||||
menu_handler=menu_handler,
|
||||
steamdeck=self.system_info.get('is_steamdeck', False)
|
||||
)
|
||||
|
||||
confirmed_context = modlist_cli.run_discovery_phase(context_override=context)
|
||||
if confirmed_context:
|
||||
menu_handler.logger.info("Tuxborn discovery confirmed by GUI. Proceeding to configuration/installation.")
|
||||
modlist_cli.configuration_phase()
|
||||
|
||||
# Handle GUI integration prompts (copied from original)
|
||||
print('[PROMPT:RESTART_STEAM]')
|
||||
if os.environ.get('JACKIFY_GUI_MODE'):
|
||||
input() # Wait for GUI to send confirmation, no CLI prompt
|
||||
else:
|
||||
answer = input('Restart Steam automatically now? (Y/n): ')
|
||||
# ... handle answer as before ...
|
||||
|
||||
print('[PROMPT:MANUAL_STEPS]')
|
||||
if os.environ.get('JACKIFY_GUI_MODE'):
|
||||
input() # Wait for GUI to send confirmation, no CLI prompt
|
||||
else:
|
||||
input('Once you have completed ALL the steps above, press Enter to continue...')
|
||||
|
||||
return 0
|
||||
else:
|
||||
menu_handler.logger.info("Tuxborn discovery/confirmation cancelled or failed (GUI mode).")
|
||||
print(f"{COLOR_INFO}Tuxborn installation cancelled or not confirmed.{COLOR_RESET}")
|
||||
return 1
|
||||
@@ -21,11 +21,9 @@ from jackify import __version__ as jackify_version
|
||||
# Import our command handlers
|
||||
from .commands.configure_modlist import ConfigureModlistCommand
|
||||
from .commands.install_modlist import InstallModlistCommand
|
||||
from .commands.tuxborn import TuxbornCommand
|
||||
|
||||
# Import our menu handlers
|
||||
from .menus.main_menu import MainMenuHandler
|
||||
from .menus.tuxborn_menu import TuxbornMenuHandler
|
||||
from .menus.wabbajack_menu import WabbajackMenuHandler
|
||||
from .menus.hoolamike_menu import HoolamikeMenuHandler
|
||||
from .menus.additional_menu import AdditionalMenuHandler
|
||||
@@ -280,7 +278,6 @@ class JackifyCLI:
|
||||
commands = {
|
||||
'configure_modlist': ConfigureModlistCommand(self.backend_services),
|
||||
'install_modlist': InstallModlistCommand(self.backend_services, self.system_info),
|
||||
'tuxborn': TuxbornCommand(self.backend_services, self.system_info)
|
||||
}
|
||||
return commands
|
||||
|
||||
@@ -292,7 +289,6 @@ class JackifyCLI:
|
||||
"""
|
||||
menus = {
|
||||
'main': MainMenuHandler(dev_mode=getattr(self, 'dev_mode', False)),
|
||||
'tuxborn': TuxbornMenuHandler(),
|
||||
'wabbajack': WabbajackMenuHandler(),
|
||||
'hoolamike': HoolamikeMenuHandler(),
|
||||
'additional': AdditionalMenuHandler()
|
||||
@@ -371,10 +367,6 @@ class JackifyCLI:
|
||||
self._debug_print('Entering restart_steam workflow')
|
||||
return self._handle_restart_steam()
|
||||
|
||||
# Handle Tuxborn auto mode
|
||||
if getattr(self.args, 'tuxborn_auto', False):
|
||||
self._debug_print('Entering Tuxborn workflow')
|
||||
return self.commands['tuxborn'].execute(self.args)
|
||||
|
||||
# Handle install-modlist top-level functionality
|
||||
if getattr(self.args, 'install_modlist', False):
|
||||
@@ -404,7 +396,6 @@ class JackifyCLI:
|
||||
parser.add_argument('--update', action='store_true', help='Check for and install updates')
|
||||
|
||||
# Add command-specific arguments
|
||||
self.commands['tuxborn'].add_args(parser)
|
||||
self.commands['install_modlist'].add_top_level_args(parser)
|
||||
|
||||
# Add subcommands
|
||||
@@ -459,8 +450,6 @@ class JackifyCLI:
|
||||
return 0
|
||||
elif choice == "wabbajack":
|
||||
self.menus['wabbajack'].show_wabbajack_tasks_menu(self)
|
||||
elif choice == "tuxborn":
|
||||
self.menus['tuxborn'].show_tuxborn_installer_menu(self)
|
||||
# HIDDEN FOR FIRST RELEASE - UNCOMMENT WHEN READY
|
||||
# elif choice == "hoolamike":
|
||||
# self.menus['hoolamike'].show_hoolamike_menu(self)
|
||||
|
||||
@@ -4,7 +4,6 @@ Extracted from the legacy monolithic CLI system
|
||||
"""
|
||||
|
||||
from .main_menu import MainMenuHandler
|
||||
from .tuxborn_menu import TuxbornMenuHandler
|
||||
from .wabbajack_menu import WabbajackMenuHandler
|
||||
from .hoolamike_menu import HoolamikeMenuHandler
|
||||
from .additional_menu import AdditionalMenuHandler
|
||||
@@ -12,7 +11,6 @@ from .recovery_menu import RecoveryMenuHandler
|
||||
|
||||
__all__ = [
|
||||
'MainMenuHandler',
|
||||
'TuxbornMenuHandler',
|
||||
'WabbajackMenuHandler',
|
||||
'HoolamikeMenuHandler',
|
||||
'AdditionalMenuHandler',
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
"""
|
||||
Tuxborn Menu Handler for Jackify CLI Frontend
|
||||
Extracted from src.modules.menu_handler.MenuHandler.show_tuxborn_installer_menu()
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from jackify.shared.colors import (
|
||||
COLOR_SELECTION, COLOR_RESET, COLOR_INFO, COLOR_PROMPT, COLOR_WARNING
|
||||
)
|
||||
from jackify.shared.ui_utils import print_jackify_banner
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
|
||||
class TuxbornMenuHandler:
|
||||
"""
|
||||
Handles the Tuxborn Automatic Installer workflow
|
||||
Extracted from legacy MenuHandler class
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.logger = None # Will be set by CLI when needed
|
||||
|
||||
def show_tuxborn_installer_menu(self, cli_instance):
|
||||
"""
|
||||
Implements the Tuxborn Automatic Installer workflow.
|
||||
Prompts for install path, downloads path, and Nexus API key, then runs the one-shot install from start to finish
|
||||
|
||||
Args:
|
||||
cli_instance: Reference to main CLI instance for access to handlers
|
||||
"""
|
||||
# Import backend service
|
||||
from jackify.backend.core.modlist_operations import ModlistInstallCLI
|
||||
|
||||
print_jackify_banner()
|
||||
print(f"{COLOR_SELECTION}Tuxborn Automatic Installer{COLOR_RESET}")
|
||||
print(f"{COLOR_SELECTION}{'-'*32}{COLOR_RESET}")
|
||||
print(f"{COLOR_INFO}This will install the Tuxborn modlist using the custom Jackify Install Engine in one automated flow.{COLOR_RESET}")
|
||||
print(f"{COLOR_INFO}You will be prompted for the install location, downloads directory, and your Nexus API key.{COLOR_RESET}\n")
|
||||
|
||||
tuxborn_machineid = "Tuxborn/Tuxborn"
|
||||
tuxborn_modlist_name = "Tuxborn"
|
||||
|
||||
# Prompt for install directory
|
||||
print("----------------------------")
|
||||
config_handler = ConfigHandler()
|
||||
base_install_dir = Path(config_handler.get_modlist_install_base_dir())
|
||||
default_install_dir = base_install_dir / "Skyrim" / "Tuxborn"
|
||||
print(f"{COLOR_PROMPT}Please enter the directory you wish to use for Tuxborn installation.{COLOR_RESET}")
|
||||
print(f"(Default: {default_install_dir})")
|
||||
install_dir_result = self._get_directory_path_legacy(
|
||||
cli_instance,
|
||||
prompt_message=f"{COLOR_PROMPT}Install directory (Enter for default, 'q' to cancel): {COLOR_RESET}",
|
||||
default_path=default_install_dir,
|
||||
create_if_missing=True,
|
||||
no_header=True
|
||||
)
|
||||
if not install_dir_result:
|
||||
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
|
||||
input("Press Enter to return to the main menu...")
|
||||
return
|
||||
if isinstance(install_dir_result, tuple):
|
||||
install_dir, _ = install_dir_result # We'll use the path, creation handled by engine or later
|
||||
else:
|
||||
install_dir = install_dir_result
|
||||
|
||||
# Prompt for download directory
|
||||
print("----------------------------")
|
||||
base_download_dir = Path(config_handler.get_modlist_downloads_base_dir())
|
||||
default_download_dir = base_download_dir / "Tuxborn"
|
||||
print(f"{COLOR_PROMPT}Please enter the directory you wish to use for Tuxborn downloads.{COLOR_RESET}")
|
||||
print(f"(Default: {default_download_dir})")
|
||||
download_dir_result = self._get_directory_path_legacy(
|
||||
cli_instance,
|
||||
prompt_message=f"{COLOR_PROMPT}Download directory (Enter for default, 'q' to cancel): {COLOR_RESET}",
|
||||
default_path=default_download_dir,
|
||||
create_if_missing=True,
|
||||
no_header=True
|
||||
)
|
||||
if not download_dir_result:
|
||||
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
|
||||
input("Press Enter to return to the main menu...")
|
||||
return
|
||||
if isinstance(download_dir_result, tuple):
|
||||
download_dir, _ = download_dir_result # We'll use the path, creation handled by engine or later
|
||||
else:
|
||||
download_dir = download_dir_result
|
||||
|
||||
# Prompt for Nexus API key
|
||||
print("----------------------------")
|
||||
from jackify.backend.services.api_key_service import APIKeyService
|
||||
api_key_service = APIKeyService()
|
||||
saved_key = api_key_service.get_saved_api_key()
|
||||
api_key = None
|
||||
|
||||
if saved_key:
|
||||
print(f"{COLOR_INFO}A Nexus API Key is already saved.{COLOR_RESET}")
|
||||
use_saved = input(f"{COLOR_PROMPT}Use the saved API key? [Y/n]: {COLOR_RESET}").strip().lower()
|
||||
if use_saved in ('', 'y', 'yes'):
|
||||
api_key = saved_key
|
||||
else:
|
||||
new_key = input(f"{COLOR_PROMPT}Enter a new Nexus API Key (or press Enter to keep the saved one): {COLOR_RESET}").strip()
|
||||
if new_key:
|
||||
api_key = new_key
|
||||
replace = input(f"{COLOR_PROMPT}Replace the saved key with this one? [y/N]: {COLOR_RESET}").strip().lower()
|
||||
if replace == 'y':
|
||||
if api_key_service.save_api_key(api_key):
|
||||
print(f"{COLOR_INFO}API key saved successfully.{COLOR_RESET}")
|
||||
else:
|
||||
print(f"{COLOR_WARNING}Failed to save API key. Using for this session only.{COLOR_RESET}")
|
||||
else:
|
||||
print(f"{COLOR_INFO}Using new key for this session only. Saved key unchanged.{COLOR_RESET}")
|
||||
else:
|
||||
api_key = saved_key
|
||||
else:
|
||||
print(f"{COLOR_PROMPT}A Nexus Mods API key is required for downloading mods.{COLOR_RESET}")
|
||||
print(f"{COLOR_INFO}You can get your personal key at: {COLOR_SELECTION}https://www.nexusmods.com/users/myaccount?tab=api{COLOR_RESET}")
|
||||
print(f"{COLOR_WARNING}Your API Key is NOT saved locally. It is used only for this session unless you choose to save it.{COLOR_RESET}")
|
||||
api_key = input(f"{COLOR_PROMPT}Enter Nexus API Key (or 'q' to cancel): {COLOR_RESET}").strip()
|
||||
if not api_key or api_key.lower() == 'q':
|
||||
print(f"{COLOR_INFO}Cancelled by user.{COLOR_RESET}")
|
||||
input("Press Enter to return to the main menu...")
|
||||
return
|
||||
save = input(f"{COLOR_PROMPT}Would you like to save this API key for future use? [y/N]: {COLOR_RESET}").strip().lower()
|
||||
if save == 'y':
|
||||
if api_key_service.save_api_key(api_key):
|
||||
print(f"{COLOR_INFO}API key saved successfully.{COLOR_RESET}")
|
||||
else:
|
||||
print(f"{COLOR_WARNING}Failed to save API key. Using for this session only.{COLOR_RESET}")
|
||||
else:
|
||||
print(f"{COLOR_INFO}Using API key for this session only. It will not be saved.{COLOR_RESET}")
|
||||
|
||||
# Context for ModlistInstallCLI
|
||||
context = {
|
||||
'machineid': tuxborn_machineid,
|
||||
'modlist_name': tuxborn_modlist_name, # Will be used for shortcut name
|
||||
'install_dir': install_dir_result, # Pass tuple (path, create_flag) or path
|
||||
'download_dir': download_dir_result, # Pass tuple (path, create_flag) or path
|
||||
'nexus_api_key': api_key,
|
||||
'resolution': None
|
||||
}
|
||||
|
||||
modlist_cli = ModlistInstallCLI(self, getattr(cli_instance, 'steamdeck', False))
|
||||
|
||||
# run_discovery_phase will use context_override, display summary, and ask for confirmation.
|
||||
# If user confirms, it returns the context, otherwise None.
|
||||
confirmed_context = modlist_cli.run_discovery_phase(context_override=context)
|
||||
|
||||
if confirmed_context:
|
||||
if self.logger:
|
||||
self.logger.info("Tuxborn discovery confirmed by user. Proceeding to configuration/installation.")
|
||||
# The modlist_cli instance now holds the confirmed context.
|
||||
# configuration_phase will use modlist_cli.context
|
||||
modlist_cli.configuration_phase()
|
||||
# After configuration_phase, messages about success or next steps are handled within it or by _configure_new_modlist
|
||||
else:
|
||||
if self.logger:
|
||||
self.logger.info("Tuxborn discovery/confirmation cancelled or failed.")
|
||||
print(f"{COLOR_INFO}Tuxborn installation cancelled or not confirmed.{COLOR_RESET}")
|
||||
input(f"{COLOR_PROMPT}Press Enter to return to the main menu...{COLOR_RESET}")
|
||||
return
|
||||
|
||||
def _get_directory_path_legacy(self, cli_instance, prompt_message: str, default_path: Optional[Path],
|
||||
create_if_missing: bool = True, no_header: bool = False) -> Optional[Path]:
|
||||
"""
|
||||
LEGACY BRIDGE: Delegate to legacy menu handler until full backend migration
|
||||
|
||||
Args:
|
||||
cli_instance: Reference to main CLI instance
|
||||
prompt_message: The prompt to show user
|
||||
default_path: Default path if user presses Enter
|
||||
create_if_missing: Whether to create directory if it doesn't exist
|
||||
no_header: Whether to skip header display
|
||||
|
||||
Returns:
|
||||
Path object or None if cancelled
|
||||
"""
|
||||
# LEGACY BRIDGE: Use the original menu handler's method
|
||||
if hasattr(cli_instance, 'menu') and hasattr(cli_instance.menu, 'get_directory_path'):
|
||||
return cli_instance.menu.get_directory_path(
|
||||
prompt_message=prompt_message,
|
||||
default_path=default_path,
|
||||
create_if_missing=create_if_missing,
|
||||
no_header=no_header
|
||||
)
|
||||
else:
|
||||
# Fallback: simple input for now (will be replaced in future phases)
|
||||
response = input(prompt_message).strip()
|
||||
if response.lower() == 'q':
|
||||
return None
|
||||
elif response == '':
|
||||
return default_path
|
||||
else:
|
||||
return Path(response)
|
||||
Reference in New Issue
Block a user