Coverage for / home / runner / work / bijux-cli / bijux-cli / src / bijux_cli / cli / plugins / commands / list.py: 96%
24 statements
« prev ^ index » next coverage.py v7.13.2, created at 2026-01-26 17:59 +0000
« prev ^ index » next coverage.py v7.13.2, created at 2026-01-26 17:59 +0000
1# SPDX-License-Identifier: Apache-2.0
2# Copyright © 2025 Bijan Mousavi
4"""Implements the `plugins list` subcommand for the Bijux CLI.
6This module provides the primary command for listing all installed CLI plugins.
7It performs security checks on the plugins directory and then delegates its
8core logic to the shared `handle_list_plugins` utility, which scans the
9filesystem and returns a structured list.
11Output Contract:
12 * Success: `{"plugins": [{"name": str, "version": str, "enabled": bool}, ...]}`
13 * Error: `{"error": str, "code": int}`
15Exit Codes:
16 * `0`: Success.
17 * `1`: An error occurred while accessing the plugins directory (e.g.,
18 it is a symlink or inaccessible).
19 * `2`: An invalid flag was provided (e.g., bad format).
20 * `3`: An ASCII or encoding error was detected in the environment.
21"""
23from __future__ import annotations
25import platform
27import typer
29from bijux_cli.cli.core.command import new_run_command, validate_common_flags
30from bijux_cli.cli.core.constants import (
31 OPT_FORMAT,
32 OPT_LOG_LEVEL,
33 OPT_PRETTY,
34 OPT_QUIET,
35)
36from bijux_cli.cli.core.help_text import (
37 HELP_FORMAT,
38 HELP_LOG_LEVEL,
39 HELP_NO_PRETTY,
40 HELP_QUIET,
41)
42from bijux_cli.cli.plugins.commands.validation import refuse_on_symlink
43from bijux_cli.core.precedence import current_execution_policy
44from bijux_cli.plugins import get_plugins_dir
45from bijux_cli.plugins.catalog import list_installed_plugins
48def list_plugin(
49 quiet: bool = typer.Option(False, *OPT_QUIET, help=HELP_QUIET),
50 fmt: str = typer.Option("json", *OPT_FORMAT, help=HELP_FORMAT),
51 pretty: bool = typer.Option(True, OPT_PRETTY, help=HELP_NO_PRETTY),
52 log_level: str = typer.Option("info", *OPT_LOG_LEVEL, help=HELP_LOG_LEVEL),
53) -> None:
54 """Lists all installed CLI plugins.
56 This command first performs security checks on the plugins directory, such
57 as ensuring it is not a symbolic link. It then delegates to the shared
58 `handle_list_plugins` utility to perform the filesystem scan and emit the
59 structured output.
61 Args:
62 quiet (bool): If True, suppresses all output except for errors.
63 fmt (str): The output format, "json" or "yaml".
64 pretty (bool): If True, pretty-prints the output.
65 log_level (str): The requested logging level.
67 Returns:
68 None:
70 Raises:
71 SystemExit: Always exits with a contract-compliant status code and
72 payload, indicating success or detailing an error.
73 """
74 command = "plugins list"
76 effective = current_execution_policy()
77 validate_common_flags(
78 fmt,
79 command,
80 effective.quiet,
81 include_runtime=effective.include_runtime,
82 log_level=effective.log_level,
83 )
84 plugins_dir = get_plugins_dir()
85 refuse_on_symlink(
86 plugins_dir,
87 command,
88 effective.output_format,
89 effective.quiet,
90 effective.log_level,
91 )
92 plugins = list_installed_plugins()
94 def payload_builder(include_runtime: bool) -> dict[str, object]:
95 """Build plugin list payload with optional runtime metadata."""
96 payload: dict[str, object] = {"plugins": plugins}
97 if include_runtime: 97 ↛ 100line 97 didn't jump to line 100 because the condition on line 97 was always true
98 payload["python"] = platform.python_version()
99 payload["platform"] = platform.platform()
100 return payload
102 new_run_command(
103 command_name=command,
104 payload_builder=payload_builder,
105 quiet=effective.quiet,
106 fmt=effective.output_format,
107 pretty=effective.pretty,
108 log_level=effective.log_level,
109 )