githrun

A CLI tool to run Python scrip...
Log | Files | Refs | README | LICENSE

cli.py (7013B)


      1 import typer
      2 from rich.console import Console
      3 from rich.syntax import Syntax
      4 from rich.table import Table
      5 from rich.prompt import Prompt
      6 from typing import Optional
      7 
      8 # Import the version from __init__.py
      9 from . import __version__
     10 
     11 from .core import (
     12     execute_remote_code, 
     13     search_repository, 
     14     get_folder_contents, 
     15     download_file, 
     16     download_folder,
     17     install_tool,
     18     login_github,
     19     add_bookmark,
     20     list_bookmarks
     21 )
     22 from .network import RateLimitError, fetch_url_content, fetch_gist_content, convert_to_raw_url
     23 from .utils import print_error, print_info, print_warning, print_success, ConfigManager
     24 
     25 app = typer.Typer(help="Githrun: Run Python code from GitHub instantly.")
     26 bookmark_app = typer.Typer(help="Manage bookmarks.")
     27 app.add_typer(bookmark_app, name="bookmark")
     28 
     29 console = Console()
     30 
     31 # --- VERSION HANDLING ---
     32 
     33 def version_callback(value: bool):
     34     if value:
     35         console.print(f"Githrun version: [bold cyan]{__version__}[/bold cyan]")
     36         raise typer.Exit()
     37 
     38 @app.callback()
     39 def main(
     40     version: bool = typer.Option(
     41         None, 
     42         "--version", 
     43         "-v", 
     44         help="Show the application's version and exit.", 
     45         callback=version_callback, 
     46         is_eager=True
     47     )
     48 ):
     49     """
     50     Githrun: Run Python code from GitHub instantly.
     51     """
     52     pass
     53 
     54 # --- MAIN COMMANDS ---
     55 
     56 @app.command()
     57 def login(token: str = typer.Argument(..., help="Your GitHub Personal Access Token.")):
     58     """
     59     Authenticate with GitHub to increase rate limits and access private repos.
     60     """
     61     login_github(token)
     62     print_success("GitHub token saved successfully!")
     63 
     64 @app.command()
     65 def install(
     66     url: str = typer.Argument(..., help="URL of the script."),
     67     name: str = typer.Option(..., "--name", "-n", help="Name of the command tool.")
     68 ):
     69     """
     70     Install a remote script as a local command-line tool.
     71     """
     72     try:
     73         msg = install_tool(url, name)
     74         print_success(msg)
     75     except Exception as e:
     76         print_error(str(e))
     77 
     78 @app.command()
     79 def run(
     80     url: str = typer.Argument(..., help="GitHub URL, Gist URL, or Bookmark Name."),
     81     inspect: bool = typer.Option(False, "--inspect", "-i", help="Print code without running."),
     82     yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation."),
     83     auto_install: bool = typer.Option(False, "--auto-install", help="Auto-install missing dependencies.")
     84 ):
     85     """Download and execute a remote Python script."""
     86     try:
     87         # Check bookmarks
     88         b_url = ConfigManager.get_bookmark(url)
     89         if b_url:
     90             print_info(f"Resolved bookmark '{url}' to: {b_url}")
     91             url = b_url
     92 
     93         if inspect:
     94             raw = convert_to_raw_url(url)
     95             if raw.startswith("gist:"):
     96                 content = fetch_gist_content(raw.split(":")[1])
     97             else:
     98                 content = fetch_url_content(raw)
     99             if content:
    100                 console.print(Syntax(content, "python", theme="monokai", line_numbers=True))
    101             return
    102 
    103         if not yes:
    104             console.print(f"\n[bold yellow]SECURITY WARNING:[/bold yellow] Running remote code from:")
    105             console.print(f"[underline]{url}[/underline]\n")
    106             if not typer.confirm("Execute this script?"):
    107                 raise typer.Exit()
    108 
    109         execute_remote_code(url, auto_install=auto_install)
    110 
    111     except RateLimitError:
    112         print_error("GitHub API Rate Limit Exceeded (60 reqs/hr).")
    113         if typer.confirm("Would you like to add an API Key to increase limits?"):
    114             token = Prompt.ask("Enter GitHub Token")
    115             login_github(token)
    116             print_success("Token saved! Please try running the command again.")
    117     except Exception as e:
    118         print_error(str(e))
    119         raise typer.Exit(1)
    120 
    121 # --- BOOKMARK COMMANDS ---
    122 
    123 @bookmark_app.command("add")
    124 def bookmark_add(name: str, url: str):
    125     """Save a URL as a bookmark."""
    126     add_bookmark(name, url)
    127     print_success(f"Bookmark '{name}' added.")
    128 
    129 @bookmark_app.command("list")
    130 def bookmark_list():
    131     """List all saved bookmarks."""
    132     bookmarks = list_bookmarks()
    133     if not bookmarks:
    134         print_warning("No bookmarks found.")
    135         return
    136     
    137     table = Table(title="Bookmarks")
    138     table.add_column("Name", style="cyan")
    139     table.add_column("URL", style="green")
    140     for name, url in bookmarks.items():
    141         table.add_row(name, url)
    142     console.print(table)
    143 
    144 # --- EXISTING COMMANDS (UPDATED) ---
    145 
    146 @app.command()
    147 def find(
    148     repo_url: str = typer.Argument(..., help="GitHub repository URL."),
    149     query: str = typer.Argument(..., help="Search query (filename)."),
    150     interactive: bool = typer.Option(True, help="Enable interactive selection.")
    151 ):
    152     """Search for files in a repo."""
    153     try:
    154         with console.status("Scanning..."):
    155             from .core import search_repository
    156             results = search_repository(repo_url, query)
    157         
    158         if not results:
    159              print_warning("No results.")
    160              return
    161 
    162         table = Table(title=f"Results for {query}")
    163         table.add_column("#", style="yellow")
    164         table.add_column("Path", style="cyan")
    165         
    166         for idx, r in enumerate(results):
    167             table.add_row(str(idx+1), r['path'])
    168         console.print(table)
    169 
    170         if interactive:
    171             choice = Prompt.ask("Select file # to Run (or 'q')")
    172             if choice.isdigit():
    173                 idx = int(choice) - 1
    174                 if 0 <= idx < len(results):
    175                     execute_remote_code(results[idx]['raw_url'])
    176 
    177     except RateLimitError:
    178         print_error("Rate Limit Hit.")
    179         print_info("Use 'githrun login <token>' to fix this.")
    180     except Exception as e:
    181         print_error(str(e))
    182 
    183 @app.command()
    184 def download(
    185     url: str = typer.Argument(..., help="GitHub file or folder URL."),
    186     output: Optional[str] = typer.Option(None, "--output", "-o", help="Output path.")
    187 ):
    188     """Download file or folder."""
    189     try:
    190         from .core import download_folder, download_file
    191         if "/tree/" in url:
    192             path = download_folder(url, output)
    193             print_success(f"Downloaded folder: {path}")
    194         else:
    195             path = download_file(url, output)
    196             print_success(f"Downloaded file: {path}")
    197     except Exception as e:
    198         print_error(str(e))
    199 
    200 @app.command()
    201 def show(url: str = typer.Argument(..., help="GitHub folder URL.")):
    202     """Show folder contents."""
    203     try:
    204         from .core import get_folder_contents
    205         items = get_folder_contents(url)
    206         
    207         if not items:
    208             print_error("Empty folder or invalid URL.")
    209             return
    210 
    211         table = Table(title="Contents")
    212         table.add_column("Name")
    213         table.add_column("Type")
    214         
    215         for item in items:
    216             itype = "Dir" if item['type'] == 'dir' else "File"
    217             table.add_row(item['name'], itype)
    218         console.print(table)
    219     except Exception as e:
    220         print_error(str(e))
    221 
    222 if __name__ == "__main__":
    223     app()
© notamitgamer • Site Built: 2026-07-21 13:58:23 UTC • git-mirror commit: 1037f62 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror