Skip to content
v0.1.3

Repl Command API Reference

This section documents the internals of the repl command in Bijux CLI.

bijux_cli.commands.repl

Implements the interactive Read-Eval-Print Loop (REPL) for the Bijux CLI.

This module provides a rich, interactive shell for executing Bijux CLI commands. It enhances the user experience with features like persistent command history, context-aware tab-completion, and a colorized prompt. Users can chain multiple commands on a single line using semicolons. The REPL can also operate in a non-interactive mode to process commands piped from stdin.

The REPL itself operates in a human-readable format. When executing commands, it respects global flags like --format or --quiet for those specific invocations.

Exit Codes
  • 0: The REPL session was exited cleanly (e.g., via exit, quit, Ctrl+D, or a caught signal).
  • 2: An invalid flag was provided to the repl command itself (e.g., --format=json).

CommandCompleter

CommandCompleter(main_app: Typer)

Bases: Completer

Provides context-aware tab-completion for the REPL.

Initializes the completer.

Parameters:

  • main_app (Typer) –

    The root Typer application whose commands and options will be used for completion suggestions.

Source code in src/bijux_cli/commands/repl.py
def __init__(self, main_app: typer.Typer) -> None:
    """Initializes the completer.

    Args:
        main_app (typer.Typer): The root Typer application whose commands
            and options will be used for completion suggestions.
    """
    self.main_app = main_app
    self._cmd_map = self._collect(main_app)
    self._BUILTINS = _BUILTINS

get_completions

get_completions(
    document: Document, _complete_event: CompleteEvent
) -> Iterator[Completion]

Yields completion suggestions for the current input.

Parameters:

  • document (Document) –

    The current prompt_toolkit document.

  • _complete_event (CompleteEvent) –

    The completion event (unused).

Yields:

  • Completion ( Completion ) –

    A prompt_toolkit Completion object.

Source code in src/bijux_cli/commands/repl.py
def get_completions(  # pyright: ignore[reportIncompatibleMethodOverride]
    self,
    document: Document,
    _complete_event: CompleteEvent,
) -> Iterator[Completion]:
    """Yields completion suggestions for the current input.

    Args:
        document (Document): The current `prompt_toolkit` document.
        _complete_event (CompleteEvent): The completion event (unused).

    Yields:
        Completion: A `prompt_toolkit` `Completion` object.
    """
    text = document.text_before_cursor
    try:
        words: list[str] = shlex.split(text)
    except ValueError:
        return
    if text.endswith(" ") or not text:
        words.append("")
    current = words[-1]

    found = False

    if current.startswith("-"):
        for opt in GLOBAL_OPTS:
            if opt.startswith(current):
                found = True
                yield Completion(opt, start_position=-len(current))

    cmd_obj, _rem = self._find(words[:-1])
    if cmd_obj is None:
        for b in self._BUILTINS:
            if b.startswith(current):
                found = True
                yield Completion(b, start_position=-len(current))

    if cmd_obj is None:
        for key in self._cmd_map:
            if len(key) == 1 and key[0].startswith(current):
                found = True
                yield Completion(key[0], start_position=-len(current))
        return

    is_group = hasattr(cmd_obj, "registered_commands") or hasattr(
        cmd_obj, "registered_groups"
    )
    if is_group:
        names = [c.name for c in getattr(cmd_obj, "registered_commands", [])]
        names += [g.name for g in getattr(cmd_obj, "registered_groups", [])]
        for n in names:
            if n.startswith(current):
                found = True
                yield Completion(n, start_position=-len(current))

    if (not is_group) and hasattr(cmd_obj, "params"):
        for param in cmd_obj.params:
            for opt in (*param.opts, *(getattr(param, "secondary_opts", []) or [])):
                if opt.startswith(current):
                    found = True
                    yield Completion(opt, start_position=-len(current))

    if "--help".startswith(current):
        found = True
        yield Completion("--help", start_position=-len(current))

    if not found:
        if (
            len(words) >= 3
            and words[0] == "config"
            and words[1] == "set"
            and words[2] == ""
        ):
            yield Completion("KEY=VALUE", display="KEY=VALUE", start_position=0)
        elif current == "":
            yield Completion("DUMMY", display="DUMMY", start_position=0)

get_prompt

get_prompt() -> str | ANSI

Returns the REPL prompt string.

The prompt is styled with ANSI colors unless NO_COLOR or a test mode environment variable is set.

Returns:

  • str | ANSI

    str | ANSI: The prompt string, which may include ANSI color codes.

Source code in src/bijux_cli/commands/repl.py
def get_prompt() -> str | ANSI:
    """Returns the REPL prompt string.

    The prompt is styled with ANSI colors unless `NO_COLOR` or a test mode
    environment variable is set.

    Returns:
        str | ANSI: The prompt string, which may include ANSI color codes.
    """
    if os.environ.get("BIJUXCLI_TEST_MODE") == "1" or os.environ.get("NO_COLOR") == "1":
        return "bijux> "
    return ANSI("\x1b[36mbijux> \x1b[0m")

main

main(
    ctx: Context,
    quiet: bool = Option(
        False, "-q", "--quiet", help=HELP_QUIET
    ),
    verbose: bool = Option(
        False, "-v", "--verbose", help=HELP_VERBOSE
    ),
    fmt: str = Option(
        "human", "-f", "--format", help=HELP_FORMAT_HELP
    ),
    pretty: bool = Option(
        True, "--pretty/--no-pretty", help=HELP_NO_PRETTY
    ),
    debug: bool = Option(
        False, "-d", "--debug", help=HELP_DEBUG
    ),
) -> None

Defines the entrypoint for the bijux repl command.

This function initializes the REPL environment. It validates flags, sets up signal handlers for clean shutdown, and dispatches to either the non-interactive (piped) mode or the interactive async prompt loop.

Parameters:

  • ctx (Context) –

    The Typer context for the CLI.

  • quiet (bool, default: Option(False, '-q', '--quiet', help=HELP_QUIET) ) –

    If True, forces non-interactive mode and suppresses prompts and command output.

  • verbose (bool, default: Option(False, '-v', '--verbose', help=HELP_VERBOSE) ) –

    If True, enables verbose output for subcommands.

  • fmt (str, default: Option('human', '-f', '--format', help=HELP_FORMAT_HELP) ) –

    The desired output format. Only "human" is supported for the REPL itself.

  • pretty (bool, default: Option(True, '--pretty/--no-pretty', help=HELP_NO_PRETTY) ) –

    If True, enables pretty-printing for subcommands.

  • debug (bool, default: Option(False, '-d', '--debug', help=HELP_DEBUG) ) –

    If True, enables debug diagnostics for subcommands.

Returns:

  • None ( None ) –
Source code in src/bijux_cli/commands/repl.py
@repl_app.callback(invoke_without_command=True)
def main(
    ctx: typer.Context,
    quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET),
    verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE),
    fmt: str = typer.Option("human", "-f", "--format", help=HELP_FORMAT_HELP),
    pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY),
    debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG),
) -> None:
    """Defines the entrypoint for the `bijux repl` command.

    This function initializes the REPL environment. It validates flags, sets
    up signal handlers for clean shutdown, and dispatches to either the
    non-interactive (piped) mode or the interactive async prompt loop.

    Args:
        ctx (typer.Context): The Typer context for the CLI.
        quiet (bool): If True, forces non-interactive mode and suppresses
            prompts and command output.
        verbose (bool): If True, enables verbose output for subcommands.
        fmt (str): The desired output format. Only "human" is supported for
            the REPL itself.
        pretty (bool): If True, enables pretty-printing for subcommands.
        debug (bool): If True, enables debug diagnostics for subcommands.

    Returns:
        None:
    """
    if ctx.invoked_subcommand:
        return

    command = "repl"
    effective_include_runtime = (verbose or debug) and not quiet

    fmt_lower = fmt.strip().lower()

    if fmt_lower != "human":
        validate_common_flags(
            fmt_lower,
            command,
            quiet,
            include_runtime=effective_include_runtime,
        )
        emit_error_and_exit(
            "REPL only supports human format.",
            code=2,
            failure="format",
            command=command,
            fmt=fmt_lower,
            quiet=quiet,
            include_runtime=effective_include_runtime,
            debug=debug,
        )

    for sig in (
        signal.SIGINT,
        signal.SIGTERM,
        signal.SIGHUP,
        signal.SIGQUIT,
        signal.SIGUSR1,
    ):
        with suppress(Exception):
            signal.signal(sig, _exit_on_signal)

    if quiet or not sys.stdin.isatty():
        _run_piped(quiet)
    else:
        try:
            asyncio.get_event_loop()
        except RuntimeError:
            asyncio.set_event_loop(asyncio.new_event_loop())

        asyncio.run(_run_interactive())