Metadata-Version: 2.4
Name: uber-shell
Version: 1.0.0
Summary: A Host/Plugin framework for building powerful interactive REPLs in Python.
Author-email: Stephen J Mildehall <mynl@me.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/mynl/uber_shell_project
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click
Requires-Dist: prompt_toolkit
Requires-Dist: PyYAML
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: sphinx; extra == "dev"
Requires-Dist: myst-parser; extra == "dev"
Requires-Dist: furo; extra == "dev"
Requires-Dist: sphinx_rtd_theme; extra == "dev"
Dynamic: license-file

# Uber Shell

> *uber* (prefix) Forming nouns denoting an outstanding, supreme, or pre-eminent example of its kind, or a person or thing markedly surpassing others of its class or type, as uber-fan, uber-model, \[uber-shell\] etc. Frequently in fashion and popular culture contexts.

A Host/Plugin framework for building powerful interactive REPLs (Read-Eval-Print Loops) in Python.

**Uber Shell** allows you to:
1.  **Wrap** an existing `click` CLI into an interactive shell with history and auto-completion.
2.  **Combine** multiple independent CLIs into a single "Meta CLI" using Python Entry Points.
3.  **Auto-discovery** of compatible CLIs using entry-points.
3.  **Add** access to shell commands and other built-in utilities. User extensible.
4.  **Help** automatically builds summary of available commands.

It solves the "slow startup" problem by loading libraries once and running commands repeatedly in memory.

## Installation

```bash
pip install uber-shell
````

-----

## Use Case 1: The "Single App" Mode

Add an interactive loop to your existing CLI tool (e.g., `my-app`).

### 1\. In your CLI code

Import `UberShell` and register your Click group.

```python
# my_app/cli.py
import click
from uber_shell import UberShell

@click.group()
def cli():
    pass

@cli.command()
def hello():
    click.echo("Hello World!")

@cli.command()
@click.option("--debug", is_flag=True)
def uber(debug):
    """Starts the interactive shell."""
    shell = UberShell("my-app", debug)

    # Register all commands in 'cli', except 'uber' (to avoid recursion)
    shell.register_click_group(cli, exclude=["uber"])

    shell.start()

if __name__ == "__main__":
    cli()
```

### 2\. Usage

```bash
$ my-app uber
my-app > hello
Hello World!
my-app >
```

-----

## Use Case 2: The "Host/Plugin" Mode (The "Uber Uber" Shell)

Create a "Host" application that automatically discovers and runs commands from other installed packages ("Plugins", e.g. `quarto-tools`, `risk-engine`).

### Step 1: The Plugin (`quarto-tools`)

In the plugin's `pyproject.toml`, advertise the CLI entry point.

```toml
[project]
name = "quarto_tools"
# ...

# 1. Standard script (creates 'qt' executable)
[project.scripts]
qt = "quarto_tools.cli:entry"

# 2. Plugin Registration (Advertising to Uber Shell)
# The group name "great2.plugins" is an arbitrary contract you define.
[project.entry-points."uber.plugins"]
qt = "quarto_tools.cli:entry"
```

*Don't forget to `pip install -e .` to register the entry point\!*

### Step 2: The Host

Uber provides a default host application, use `load_plugins` to find everyone registered under your group name. This code can be duplicated in your own project.

```python
# uber_tools/cli.py
import click
from . import UberShell

@click.group()
def cli():
    pass

@cli.command()
@click.option("--debug", is_flag=True)
def uber(debug):
    """The uber/uber (Master) Shell."""
    shell = UberShell("great2", debug)

    # 1. Register Host commands
    shell.register_click_group(cli, exclude=["uber"])

    # 2. Auto-discover Plugins
    # This scans site-packages for anyone registered to "great2.plugins"
    shell.load_plugins("great2.plugins")

    shell.start()
```

### Step 3: The Experience

The `load_plugins` method provides  **Nested Access.** If the plugin has an `uber` command, typing the plugin name (e.g., `qt`) enters that plugin's specific sub-loop.


```bash
$ uber
uber >
uber > archivum
uber > archivum >
uber > archivum > toc       # Runs archivum directly
uber > archivum > ..        # Move up to the parent (also x, q, quit, exit)
uber >
uber > qt toc               # Enter the QT sub-shell and run toc command...
uber > qt > x               # Exit back to host
great2 >
```

-----

## Advanced Features

### Python Shortcuts

You can register raw Python functions to the shell, bypassing Click entirely.

```python
from my_lib import heavy_calculation

shell.register_command("calc", lambda line: heavy_calculation())
```

### Startup Scripts

You can pass a list of commands to run immediately on startup.

```python
@cli.command()
@click.argument("script", required=False)
def uber(script):
    initial_cmds = [script] if script else None
    shell.start(startup_commands=initial_cmds)
```

### System Fallback

If a command is not found in the registry, `UberShell` falls back to the system shell. This means you can run `dir`, `git status`, or `ripgrep` directly from the prompt.

```bash
great2 > rg "class UberShell"
```

## Built-ins

  * `cd [path]`: Change working directory (smart path completion included).
  * `cls`: Clear screen (Windows).
  * `ev`: Launch "Everything" search (Windows).
  * `pwd`: Print working directory.
  * `q` / `x` / `exit`: Quit.

## File Structure

```text
uber-shell/
├── pyproject.toml       # Dependencies: click, prompt_toolkit
├── README.md            # The documentation below
├── src/
│   └── uber_shell/
│       ├── __init__.py  # Exposes UberShell and factories
│       └── shell.py     # The core logic
├── docs/                # Documentation (maybe)
```

