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

1# SPDX-License-Identifier: Apache-2.0 

2# Copyright © 2025 Bijan Mousavi 

3 

4"""Implements the `plugins list` subcommand for the Bijux CLI. 

5 

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. 

10 

11Output Contract: 

12 * Success: `{"plugins": [{"name": str, "version": str, "enabled": bool}, ...]}` 

13 * Error: `{"error": str, "code": int}` 

14 

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""" 

22 

23from __future__ import annotations 

24 

25import platform 

26 

27import typer 

28 

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 

46 

47 

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. 

55 

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. 

60 

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. 

66 

67 Returns: 

68 None: 

69 

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" 

75 

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() 

93 

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 

101 

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 )