flatland3 0.2.1

Flatland3 terminal play client and account CLI
mod api;
mod auth;
mod auth_context;
mod capabilities;
mod character;
mod config;
mod game_session;
mod inventory;
mod login_ui;
mod play;
mod terminal;
mod trace_setup;

use clap::{Parser, Subcommand};
use play::PlayArgs;

const ABOUT: &str = "Flatland3 — terminal play client and control-plane CLI";
const LONG_ABOUT: &str = "\
Play a server-authoritative MMORPG in the terminal, or manage accounts via the control plane.

Default (no subcommand): launch the Ratatui play client (requires login + existing character).

Use `flatland3 capabilities` for a full list of commands and environment variables.";
const AFTER_HELP: &str = "\
EXAMPLES:
  flatland3 auth register --email you@example.com --password '…' --name You
  flatland3 auth login --email you@example.com --password '…'
  flatland3 character create MadSin
  flatland3 character list
  flatland3 --name MadSin
  flatland3 auth token create --name ci --scope play,read
  flatland3 capabilities              Print capabilities reference

LOCAL DEV (monorepo only):
  make dev-watch              Control plane + embedded gateway (auto-rebuild)
  make dev-play NAME=MadSin   Client (character must already exist)
  cargo run -p flatland-dev -- --bots 4   Embedded server + TUI (no CP)

SERVICES (local dev):
  make db-up && cargo run -p flatland-control-plane-bin   # HTTP :7380
  cargo run -p flatland-server                            # game :7373
  cargo run -p flatland3                                  # play

See plans/05-cli-and-developer-guide.md for HTTP API and curl examples.";

#[derive(Debug, Parser)]
#[command(
    name = "flatland3",
    version,
    about = ABOUT,
    long_about = LONG_ABOUT,
    after_help = AFTER_HELP
)]
struct Cli {
    #[arg(long, global = true, help = "JSON response envelope for subcommands")]
    json: bool,

    #[command(subcommand)]
    command: Option<Command>,

    #[command(flatten)]
    play: PlayArgs,
}

#[derive(Debug, Subcommand)]
enum Command {
    /// Print CLI capabilities, env vars, and quick reference
    Capabilities,
    /// Print play-client keybindings by category
    Keys,
    /// Account login and API token management (control plane HTTP API)
    Auth {
        #[command(subcommand)]
        command: auth::AuthCommand,
    },
    /// List or create characters (never auto-created by play)
    Character {
        #[command(subcommand)]
        command: character::CharacterCommand,
    },
    /// List character inventory (requires login)
    Inventory {
        #[arg(long)]
        character_id: Option<uuid::Uuid>,
    },
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let _ = dotenvy::dotenv();
    let cli = Cli::parse();
    if cli.command.is_none() {
        trace_setup::init_for_play()?;
    } else {
        trace_setup::init()?;
    }

    match cli.command {
        Some(Command::Capabilities) => {
            capabilities::print();
            Ok(())
        }
        Some(Command::Keys) => {
            print!("{}", flatland_tui::format_keybindings_plain());
            Ok(())
        }
        Some(Command::Auth { command }) => auth::run(command).await,
        Some(Command::Character { command }) => character::run(command, cli.json).await,
        Some(Command::Inventory { character_id }) => inventory::run(character_id, cli.json).await,
        None => {
            login_ui::run_if_needed().await?;
            let opts = game_session::prepare_remote_play(
                cli.play.name.as_deref(),
                cli.play.server,
            )
            .await?;
            let auto_reconnect = play::auto_reconnect_enabled(cli.play.no_reconnect);
            play::run_with_options(opts, auto_reconnect).await
        }
    }
}