Coverage for /home/runner/work/bijux-cli/bijux-cli/src/bijux_cli/cli.py: 100%
17 statements
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-19 23:36 +0000
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-19 23:36 +0000
1# SPDX-License-Identifier: MIT
2# Copyright © 2025 Bijan Mousavi
4"""Constructs the main `Typer` application for the Bijux CLI.
6This module serves as the primary builder for the entire CLI. It defines the
7root `Typer` app, orchestrates the registration of all core commands and
8the discovery of dynamic plugins, and sets the default behavior for when the
9CLI is invoked without any command.
10"""
12from __future__ import annotations
14import subprocess # nosec B404
15import sys
17import typer
18from typer import Context
20from bijux_cli.commands import register_commands, register_dynamic_plugins
23def maybe_default_to_repl(ctx: Context) -> None:
24 """Invokes the `repl` command if no other subcommand is specified.
26 This function is used as the root callback for the main `Typer` application.
27 It checks if a subcommand was invoked and, if not, re-executes the CLI
28 with the `repl` command.
30 Args:
31 ctx (Context): The Typer context, used to check for an invoked subcommand.
33 Returns:
34 None:
35 """
36 if ctx.invoked_subcommand is None:
37 subprocess.call([sys.argv[0], "repl"]) # noqa: S603 # nosec B603
40def build_app() -> typer.Typer:
41 """Builds and configures the root `Typer` application.
43 This factory function performs the main steps of assembling the CLI:
44 1. Creates the root `Typer` app instance.
45 2. Registers all core, built-in commands.
46 3. Discovers and registers all dynamic plugins.
47 4. Sets the default callback to launch the REPL.
49 Returns:
50 typer.Typer: The fully constructed `Typer` application.
51 """
52 app = typer.Typer(
53 help="Bijux CLI – Lean, plug-in‑driven command‑line interface.",
54 invoke_without_command=True,
55 context_settings={
56 "ignore_unknown_options": True,
57 "allow_extra_args": True,
58 },
59 )
60 register_commands(app)
61 register_dynamic_plugins(app)
62 app.callback(invoke_without_command=True)(maybe_default_to_repl)
63 return app
66app = build_app()
68__all__ = ["build_app", "app"]