`PluginManager.get_plugin()` raises `PluginNotFoundError` for unknown
plugins and never returns `None`, so the post-call `None` guards plus
their `except NotFoundError` handlers were unreachable. Drop them along
with the now-unused `NotFoundError`/`CleverAgentsError` imports. Also
drop the broad `except Exception` in `install_plugin` (the only ops it
guards — `Path()`/`.exists()`/`.is_dir()` — do not raise) and the
defensive `except CleverAgentsError` in `list_plugins` (covers only
`PluginManager()`/`list_plugins()`, both currently infallible) so the
remaining error paths are the ones the BDD suite actually exercises.
ISSUES CLOSED: #5756
Add mock-based @given steps and 14 new scenarios covering the rich
table list, show, enable, disable, remove happy paths, the abort
confirmation flow, and the description truncation branch
(plugin.py:134). The @when step now patches _get_plugin_manager via
context so PluginManager isolation works without a singleton.
ISSUES CLOSED: #5756
The mro-based UsageError check inside the Exception block already
catches BadParameter (it inherits from UsageError) — the separate
typer.BadParameter handler was redundant.
Added an in-process Behave step that calls main() directly and
captures err_console output, plus a scenario that runs
`plan use --no-such-flag` to cover the UsageError branch (subprocess
steps do not count toward unit-test coverage).
The custom _print_basic_help() omitted the global flags from --help output,
so the "Help Shows All Three Options" integration test failed even though
the flags worked. Added a "Global options:" section listing --data-dir,
--config-path, -v, --format, --version, --show-secrets.
The generic Exception handler in main() also swallowed click UsageError
(including NoSuchOption) and reported them as "Error [500] INTERNAL",
masking unknown-option messages. Typer vendors its own click, so an
isinstance check against `click.exceptions.UsageError` would miss
`typer._click.exceptions.UsageError`. Walk type(e).__mro__ for any
class named "UsageError" and reprint with e.format_message() at exit
code 2 — surfaces the "No such option: --automation-level" message
the "Plan Use Rejects Automation Level Flag" test asserts.
- Register plugin command in CLI main.py imports and add_typer calls
- Add plugin to valid_cmds list in main() to prevent "Invalid command" error
- Remove unused PluginNotFoundError import from plugin.py (F401 lint fix)
- Fix line too long in plugin.py _print_plugin function (E501 lint fix)
- Fix list_plugins to output JSON even when no plugins installed
- Remove duplicate step definitions from plugin_cli_steps.py that conflicted
with existing steps (I run, output should contain, output should be valid JSON)
- Rewrite plugin_cli.feature to test error cases that don't require pre-registered
plugins (since PluginManager is not a singleton across CLI invocations)
- Implement plugin CLI subcommand group with list, show, enable, disable, install, remove commands
- Add JSON/YAML output format support for all plugin commands
- Create Behave BDD tests for plugin CLI functionality
- Full type annotations and pyright compliance
- Supports plugin state management (ACTIVATED, DEACTIVATED, DISCOVERED, ERRORED)
Closes#5756