#!/usr/bin/env python3 """ Universal runner for NotebookLM skill scripts Ensures all scripts run with the correct virtual environment """ import os import re import sys import subprocess from pathlib import Path ALLOWED_SCRIPTS = { "ask_question.py", "notebook_manager.py", "session_manager.py", "auth_manager.py", "cleanup_manager.py", } SCRIPT_NAME_RE = re.compile(r"^[A-Za-z0-9_-]+\.py$") def get_venv_python(): """Get the virtual environment Python executable""" skill_dir = Path(__file__).parent.parent venv_dir = skill_dir / ".venv" if os.name == 'nt': # Windows venv_python = venv_dir / "Scripts" / "python.exe" else: # Unix/Linux/Mac venv_python = venv_dir / "bin" / "python" return venv_python def ensure_venv(): """Ensure virtual environment exists""" skill_dir = Path(__file__).parent.parent venv_dir = skill_dir / ".venv" setup_script = skill_dir / "scripts" / "setup_environment.py" # Check if venv exists if not venv_dir.exists(): print("šŸ”§ First-time setup: Creating virtual environment...") print(" This may take a minute...") # Run setup with system Python result = subprocess.run([sys.executable, str(setup_script)]) if result.returncode != 0: print("āŒ Failed to set up environment") sys.exit(1) print("āœ… Environment ready!") return get_venv_python() def main(): """Main runner""" if len(sys.argv) < 2: print("Usage: python run.py [args...]") print("\nAvailable scripts:") print(" ask_question.py - Query NotebookLM") print(" notebook_manager.py - Manage notebook library") print(" session_manager.py - Manage sessions") print(" auth_manager.py - Handle authentication") print(" cleanup_manager.py - Clean up skill data") sys.exit(1) script_name = sys.argv[1] script_args = sys.argv[2:] scripts_dir = (Path(__file__).parent.parent / "scripts").resolve() # Handle both "scripts/script.py" and "script.py" formats if script_name.startswith('scripts/'): # Remove the scripts/ prefix if provided script_name = script_name[8:] # len('scripts/') = 8 # Ensure .py extension if not script_name.endswith('.py'): script_name += '.py' if not SCRIPT_NAME_RE.match(script_name) or script_name not in ALLOWED_SCRIPTS: print(f"āŒ Unsupported script: {script_name}") sys.exit(1) # Get script path skill_dir = Path(__file__).parent.parent script_path = (scripts_dir / script_name).resolve() try: script_path.relative_to(scripts_dir) except ValueError: print(f"āŒ Script path escapes scripts directory: {script_name}") sys.exit(1) if not script_path.exists(): print(f"āŒ Script not found: {script_name}") print(f" Working directory: {Path.cwd()}") print(f" Skill directory: {skill_dir}") print(f" Looked for: {script_path}") sys.exit(1) # Ensure venv exists and get Python executable venv_python = ensure_venv() # Replace this runner with the selected venv Python process. try: os.execv(str(venv_python), [str(venv_python), str(script_path)] + script_args) except KeyboardInterrupt: print("\nāš ļø Interrupted by user") sys.exit(130) except Exception as e: print(f"āŒ Error: {e}") sys.exit(1) if __name__ == "__main__": main()