diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 0971734e..00bc4755 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -5620,6 +5620,8 @@ impl LiveCli { "config_load_error": payload.config_load_error, "message": payload.message, "reload_runtime": payload.reload_runtime, + "plugins": payload.plugins, + "load_failures": payload.load_failures, }))? ), } @@ -7744,6 +7746,8 @@ struct PluginsCommandPayload { reload_runtime: bool, status: &'static str, config_load_error: Option, + plugins: Vec, + load_failures: Vec, } fn plugins_command_payload_for( @@ -7758,17 +7762,30 @@ fn plugins_command_payload_for( }; let mut manager = build_plugin_manager(cwd, &loader, &runtime_config); let result = handle_plugins_slash_command(action, target, &mut manager)?; + let report = manager.installed_plugin_registry_report()?; Ok(plugins_command_payload_from_result( result, config_load_error, + &report, )) } fn plugins_command_payload_from_result( result: PluginsCommandResult, config_load_error: Option, + report: &plugins::PluginRegistryReport, ) -> PluginsCommandPayload { - let status = if config_load_error.is_some() { + let load_failures = report + .failures() + .iter() + .map(plugin_load_failure_json) + .collect::>(); + let plugins = report + .summaries() + .iter() + .map(plugin_summary_json) + .collect::>(); + let status = if config_load_error.is_some() || !load_failures.is_empty() { "degraded" } else { "ok" @@ -7785,6 +7802,8 @@ fn plugins_command_payload_from_result( reload_runtime: result.reload_runtime, status, config_load_error, + plugins, + load_failures, } } diff --git a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs index cd5e584c..2f0d4cd6 100644 --- a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs +++ b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs @@ -189,10 +189,13 @@ fn inventory_commands_emit_structured_json_when_requested() { "plugins target should be null when no plugin is targeted" ); assert_eq!(plugins["status"], "ok"); - assert!(plugins["plugins"] - .as_array() - .expect("plugins array") - .is_empty()); + let plugin_entries = plugins["plugins"].as_array().expect("plugins array"); + assert!( + plugin_entries.iter().all(|plugin| plugin["id"].is_string() + && plugin["lifecycle_state"].is_string() + && plugin["lifecycle"].is_object()), + "plugins JSON entries should carry lifecycle contract fields" + ); assert!(plugins["load_failures"] .as_array() .expect("plugin load failures array")