slave 0.4.0

Laravel-inspired CLI for Rust MCP servers: scaffold apps whose business operations are MCP tools over Streamable HTTP, with built-in auth, users, roles and Turso migrations
//! Command-line interface definition.

use std::path::PathBuf;

use clap::{Args, Parser, Subcommand};

/// A Laravel-inspired web framework for Rust.
#[derive(Debug, Parser)]
#[command(name = "slave", version, about, long_about = None)]
#[command(propagate_version = true, arg_required_else_help = true)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Command,
}

#[derive(Debug, Subcommand)]
pub enum Command {
    /// Create a new slave application
    New(NewArgs),

    /// Create a new model in a domain module
    ///
    /// Writes src/modules/<module>/model.rs. The module defaults to the model
    /// name in snake_case (`BlogPost` → `blog_post`) and is created if missing.
    #[command(name = "make:model")]
    MakeModel(MakeArgs),

    /// Create MCP tools for a domain module
    ///
    /// Writes src/modules/<module>/tools.rs with example `<name>_list` and
    /// `<name>_get` tools and registers it in src/modules/mod.rs. MCP tools are
    /// the application's only business interface. The module defaults to the
    /// name without its `Tools` suffix, in snake_case (`BlogPostTools` →
    /// `blog_post`).
    #[command(name = "make:tools")]
    MakeTools(MakeArgs),

    /// Create a web app in apps/<name> and route a domain to it
    ///
    /// `--stack axum` (default) renders HTML on the server: light and fast.
    /// `--stack dioxus` is a single-page app compiled to WebAssembly, for
    /// complex, interactive apps (needs `dx`, the Dioxus CLI). Either way the
    /// app gets its data from the MCP backend. It is registered in
    /// config/apps.toml, like Laravel's `Route::domain()`, and started by
    /// `slave serve`.
    #[command(name = "make:app")]
    MakeApp(MakeAppArgs),

    /// Create a new database migration
    ///
    /// Writes database/migrations/<timestamp>_<name>.rs and registers it in
    /// database/migrations/mod.rs. Names like `create_posts_table` start from
    /// a CREATE TABLE template; others start empty and irreversible.
    #[command(name = "make:migration")]
    MakeMigration {
        /// Migration name in snake_case, e.g. `create_users_table`
        name: String,
    },

    /// Run all pending database migrations as a new batch
    Migrate,

    /// Show which migrations have run and which are pending
    #[command(name = "migrate:status")]
    MigrateStatus,

    /// Roll back the most recent batch of migrations
    ///
    /// Refuses, changing nothing, if any migration in the batch is
    /// irreversible (its `down()` returns `None`).
    #[command(name = "migrate:rollback")]
    MigrateRollback,

    /// List the domain routes in config/apps.toml and whether each app is up
    #[command(name = "route:list")]
    RouteList,

    /// Show which route, app and headers a host would get
    #[command(name = "route:match")]
    RouteMatch {
        /// Host to look up, e.g. `tokox.localhost`
        host: String,
    },

    /// Run the MCP backend, the domain gateway and every app in apps/
    Serve(ServeArgs),
}

#[derive(Debug, Args)]
pub struct NewArgs {
    /// Name of the application (also used as the directory and package name)
    pub name: String,

    /// Depend on a local checkout of slave-framework instead of crates.io
    #[arg(long, value_name = "PATH")]
    pub framework_path: Option<PathBuf>,
}

#[derive(Debug, Args)]
pub struct MakeArgs {
    /// Type name in PascalCase, e.g. `Post` or `PostTools`
    pub name: String,

    /// Domain module to generate into, in snake_case (default: derived from the name)
    #[arg(short, long, visible_alias = "domain", value_name = "MODULE")]
    pub module: Option<String>,

    /// Overwrite the file with a fresh template if it already exists
    #[arg(short, long)]
    pub force: bool,
}

#[derive(Debug, Args)]
pub struct MakeAppArgs {
    /// App name, used as apps/<name> and its package name, e.g. `dashboard`
    pub name: String,

    /// Host routed to the app, e.g. `admin.localhost` or `{toko}.localhost`
    #[arg(long, value_name = "HOST")]
    pub domain: String,

    /// Port the app listens on [default: the next free port from 4000]
    #[arg(long)]
    pub port: Option<u16>,

    /// How the app renders
    #[arg(long, value_enum, default_value_t = Stack::Axum)]
    pub stack: Stack,
}

/// The kind of web app `make:app` creates.
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum Stack {
    /// Server-rendered HTML with Axum: fast, simple pages
    Axum,
    /// A Dioxus single-page app (WebAssembly): complex, interactive UIs
    Dioxus,
}

#[derive(Debug, Args)]
pub struct ServeArgs {
    /// Host address for the MCP backend [default: APP_HOST from the environment or .env, else 127.0.0.1]
    #[arg(long)]
    pub host: Option<String>,

    /// Port for the MCP backend [default: APP_PORT from the environment or .env, else 3000]
    #[arg(short, long)]
    pub port: Option<u16>,

    /// Build and run everything in release mode
    #[arg(long)]
    pub release: bool,
}