# CLI reference

`banto` is the only command you need to install. It scaffolds a project,
compiles your `.banto` files, formats them, manages your image/audio
assets, and publishes your game to [banto.tv](https://banto.tv).

If you've never used it, start with [Getting started](./getting-started.md);
this doc is the keep-it-open-while-you-work reference.

## Install

```bash
npm install -g @bantohq/cli
banto --help
```

You should see the subcommands: `init`, `build`, `fmt`, `auth`,
`publish`, and `assets`.

You can also install per-project (`npm install --save-dev @bantohq/cli`)
and invoke as `npx banto …`.

> **Get the editor extension too.** The **Banto DSL** VS Code extension
> gives you syntax highlighting, inline diagnostics, and completions as
> you type — it's the fastest way to catch mistakes before you build.
> See [Getting started → the extension](./getting-started.md#2-install-the-vs-code-extension).

## The workflow at a glance

Your game runs on banto.tv, but you can test it without real phones
using the **sandbox**. The loop is:

1. **Write** `.banto` files (the editor extension flags errors live).
2. **`banto build`** to typecheck and catch anything the editor missed.
3. **`banto publish`** — publish as a *private* game (`"public": false`)
    while you iterate.
4. **`banto sandbox`** — open the owner-only sandbox: the host screen
    plus simulated player windows in one browser, no phones needed.
5. Edit, re-`publish` (it updates in place), reload the sandbox, repeat.
    Host it live on [banto.tv](https://banto.tv) from a few phones/tabs
    for a real multi-device round, and flip `"public": true` to list it.

## Subcommands

### `banto init <dir>`

Scaffolds a new project at `./<dir>`. The directory must not already
exist (or must be empty). The starter is the **empty lobby** template:
host shows the room code with a kick list, players see a "you're in"
message.

```bash
banto init my-game
```

Files created:

```text
my-game/
├── globals.banto
├── start.banto
├── styles.banto.css
├── banto.config.json
└── .gitignore
```

### `banto build`

Compiles the project to a single `game.json` IR file at the project
root. Run it from anywhere inside the project — the CLI walks up
until it finds `globals.banto`.

```bash
banto build              # quiet
banto build --verbose    # print per-file progress
```

Output on success:

```text
Wrote ./game.json (487 bytes, 0 error(s), 0 warning(s)).
```

On a syntax/type error, you get the same diagnostics your editor
shows, then a non-zero exit code:

```text
start.banto:14:17 error [banto.cstate-write-interleaved]: …
Compilation aborted: 1 error(s), 0 warning(s).
```

`game.json` is a build artifact — keep it out of version control
(the starter's `.gitignore` already does this). `banto publish` builds
for you, so you rarely need to run `build` by hand except to verify a
change compiles.

### `banto fmt [path]`

Formats `.banto` files in place using the canonical formatter — the
same output as *Format Document* in the editor.

```bash
banto fmt                 # format every .banto file under the current dir
banto fmt start.banto     # format a single file
banto fmt --check         # report unformatted files, exit 1 if any (CI-friendly)
```

| Option          | What it does |
|-----------------|--------------|
| `-c`, `--check` | Don't rewrite anything — just list files that aren't formatted and exit `1` if there are any. |

`path` may be a file or a directory (defaults to `.`).

### `banto auth`

Logs you into [banto.tv](https://banto.tv) using a browser-based
loopback flow:

1. The CLI starts a tiny local web server.
2. It opens your browser to a banto.tv consent page.
3. You approve, banto.tv redirects to `localhost`, and the CLI
    captures the token.

```bash
banto auth
```

You only need to run this explicitly the first time. `banto publish`
and `banto assets` will trigger the same flow automatically if your
token is missing or expired.

### `banto publish`

Compiles your project and ships it to banto.tv. The settings in
`banto.config.json` (title, description, default datasets) become
your game's listing.

```bash
banto publish
```

Re-running `banto publish` after edits **updates the existing game in
place** — same ID, no duplicate listing. The CLI uses
`banto.lock.json` (sibling of `banto.config.json`) to remember which
remote game your project is bound to.

Publish with `"public": false` while you iterate — the game is
reachable only by you (and anyone you send a direct link) so you can
host and playtest it privately. Set `"public": true` to list it in the
public catalog.

> Publishing is **admin-gated** on banto.tv by default. If you get a
> 403, your account doesn't have publishing permissions.

### `banto sandbox`

Opens the **sandbox** for your currently-published game in the browser —
an owner-only test harness that runs the real game with the host screen
and simulated player windows side by side, so you can play a full round
without real phones.

```bash
banto sandbox            # default layout (host + 2 player windows)
banto sandbox -t 3       # a different layout preset
```

- `-t, --template <0-3>` picks a layout preset (how many player windows
  to tile); defaults to `2`. Use **+ Add player** in the UI to add or
  drop players live.
- The sandbox opens the **published** build, so run `banto publish`
  first (and again after each change, then reload).
- It's **owner-gated** — the browser must be signed in to banto.tv as
  the project's owner (the account from `banto auth`).
- Click the **Inspector** button (top-right) for a live view of every
  `var.*` and state-param, the datasets, each window's `cstate`, and a
  **Logs** panel showing your `func.log(...)` output and any runtime
  errors.

### `banto assets`

Manage the custom image/audio assets your game renders with
`cpnt.image`, `cpnt.audio`, or `client.playSound`. Drop media files in
an `assets/` directory at your project root; reference them from
`.banto` as `asst.<name>` (the file's base name, no extension). See
[Custom image & audio assets](./language-guide.md#custom-image--audio-assets)
for the language side.

```bash
banto assets push        # upload new/changed files from ./assets
banto assets ls          # list this project's assets + moderation state
banto assets rm <name|id># delete an owned asset (by base name or id)
banto assets catalog     # open the public asset catalog in your browser
```

| Subcommand         | What it does |
|--------------------|--------------|
| `push [dir]`       | Uploads new or changed files from `./assets` (or `dir`) to your account, records their ids in `banto.lock.json`, and reconciles moderation status. |
| `ls`               | Lists each asset with its kind, moderation status, and id. |
| `rm <name\|id>`    | Deletes one of your assets from the server and the lock file. |
| `catalog`          | Opens the shared, admin-approved asset catalog on banto.tv. |

Titles come from an `assets` map in `banto.config.json`, keyed by the
file's base name:

```json
{
    "assets": {
        "logo": { "title": "Game logo" },
        "victory-fanfare": { "title": "Victory fanfare" }
    }
}
```

> **Moderation.** Uploaded assets start **pending** and are visible
> only in your own games until an admin approves them. `asst.<name>`
> references still build and render for you while pending — approval
> just makes the asset usable by others and listable in the catalog.

## Config files

### `banto.config.json` (per project)

How your game appears on banto.tv. Lives next to `globals.banto`.

```json
{
    "title": "Grant's Trivia Tournament",
    "description": "It's like Trivia, but as a tournament!",
    "public": false,
    "default": {
        "questionSet": "this"
    },
    "new": {
        "questionSet": [
            {
                "prompt": "Which of these are programming languages?",
                "options": ["Python", "HTML", "JavaScript", "HTTP"],
                "correctOptions": [0, 2]
            }
        ]
    },
    "assets": {
        "logo": { "title": "Game logo" }
    }
}
```

| Field         | What it does |
|---------------|--------------|
| `title`       | Display name on the banto.tv listing. |
| `description` | Short blurb on the listing page. |
| `public`      | `false` = only you and people with a direct link can find it. `true` = listed in the public catalog. |
| `default.*`   | For each `data` block your project declares (`questionSet`, `promptSet`, or `dynamic`), the data source pre-selected when a host starts a game. Use a banto.tv data-source ID, or the literal `"this"` to point at the matching `new.*` entry. |
| `new.*`       | Inline contents of a fresh data source to create on banto.tv as part of publishing. Shape matches the `data` block: questions for `questionSet`, strings for `promptSet`, or a `(string \| number \| (string \| number \| (string \| number)[])[])[]` list for `dynamic`. |
| `assets.*`    | Titles for your custom assets, keyed by the media file's base name. Used by `banto assets push`. |

### Where credentials live

`banto auth` saves your bearer token outside the project, scoped to
your OS user:

| OS              | Path                            |
|-----------------|---------------------------------|
| Linux / macOS   | `~/.config/banto/`              |
| Windows         | `%APPDATA%\banto\`              |

The folder holds `credentials.json` — your bearer token. Treat it like
a password. You don't normally need to edit it by hand; `banto auth`
writes it for you.

`banto.lock.json` (next to `banto.config.json`, in your project) is
different: it remembers which remote game and assets this project is
bound to. Commit it — it's how re-publishing updates in place instead
of creating a duplicate.

## Troubleshooting

| Symptom | Fix |
|---|---|
| `banto build` says "no `globals.banto` found in this directory or any parent" | You're not inside a Banto project. `cd` into one or run `banto init <name>`. |
| `banto build` reports `unknown-asset` | You referenced `asst.<name>` for a file you haven't pushed yet. Run `banto assets push`, or check the base name matches a file in `assets/`. |
| `banto publish` returns 403 | Your account isn't allowed to publish. Publishing is admin-gated by default. |
| `banto init` says "target is not empty" | Pick a different name, or move the existing files out. There's no `--force` flag. |
| The browser-opened consent page errors out | Re-run `banto auth`. The loopback listener uses a random port and the previous attempt's port may have been blocked. |