# Setting up a CLI with Typer

I'd used `argparse` before. It's clunky - lots of boilerplate for even simple commands. Typer works differently. You write a normal function with type annotated parameters. Typer builds the CLI from the signature.

## A basic command

Here's the `init` command as it shipped with v0.1.0:

```python
from pathlib import Path
from typer import Typer
from typer import Argument as typer_argument
from typer import Option as typer_option

app = Typer(help="Scaffold a new project locally")

@app.command("init")
def init(
    project_name: str = typer_argument(
        ..., help="Name of the new project"
    ),
    path: Path = typer_option(
        Path("."), help="Where to create the project"
    ),
) -> None:
    """Scaffold a new project locally."""
    ...
```

No parser setup. No `add_argument` calls. The function signature is the whole CLI definition.

## Arguments vs options

Typer splits inputs into two kinds: arguments and options.

`project_name` uses `typer_argument`. Arguments are positional and required by default. Run `scaffoldr init myproject`, and `myproject` fills that slot. The `...` as the first value tells Typer that this argument has no default - it must be provided.

`path` uses `typer_option`. Options use `--flag value` syntax, like `scaffoldr init myproject --path ~/projects`. Options always have a default, set by the first value passed to `typer_option`. Skip the flag, and it falls back to `Path(".")`.

## Type hints drive validation

Look at `path: Path`. Typer reads that type hint. It converts whatever the user types into a `pathlib.Path` object. No conversion needed. It arrives as the right type inside the function.

Booleans work the same way. For example:

```python
protect: bool = typer_option(
    True, help="Enable branch protection on main."
)
```

Typer creates `--protect` and `--no-protect` flags automatically. No extra code for that either.

## Docstrings become help text

The docstring under the function - `"""Scaffold a new project locally."""` - isn't just for developers reading the code. Typer shows it when someone runs `scaffoldr init --help`. The `help=` argument on `typer_argument` and `typer_option` works the same way. It shows per-parameter help text.

The function signature and docstring are the only source of truth for the CLI's documentation.

## Registering the entry point

Once the function is decorated with `@app.command("init")`, it needs to be wired into the installable CLI. In `pyproject.toml`:

```toml
[project.scripts]
scaffoldr = "scaffoldr.main:app"
```

This tells Python's packaging tools to create a `scaffoldr` executable. It runs the app object from `scaffoldr/main.py`. `app` is the Typer instance every command gets registered to.

## What's next

With the CLI shell in place, the next problem was defining what gets created when someone runs `scaffoldr init` - the folder structure and files that make up a scaffolded project. In v0.1.0, this was hardcoded. That's covered in the next post.
