Codex Handbook
tui/src/app/plugin_mentions.rs 147 lines
//! Plugin mention capability enrichment for the TUI.//!//! Mention inventory comes from app-server `plugin/list`, matching the GUI//! client. The current API exposes plugin-level mention metadata there, but not//! effective per-session capability summaries.use super::background_requests::request_plugin_list;use super::*;use codex_app_server_protocol::PluginAvailability;use codex_app_server_protocol::PluginListResponse;use codex_app_server_protocol::PluginSummary;use codex_plugin::PluginCapabilitySummary;pub(super) async fn fetch_plugin_mentions(    request_handle: AppServerRequestHandle,    cwd: PathBuf,) -> Result<Vec<PluginCapabilitySummary>> {    let response = request_plugin_list(request_handle, cwd).await?;    Ok(plugin_mentions_from_list_response(response))}fn plugin_mentions_from_list_response(    response: PluginListResponse,) -> Vec<PluginCapabilitySummary> {    response        .marketplaces        .into_iter()        .flat_map(|marketplace| {            let marketplace_name = marketplace.name;            marketplace                .plugins                .into_iter()                .filter_map(move |plugin| plugin_mention_from_summary(&marketplace_name, plugin))        })        .collect()}fn plugin_is_eligible_for_mentions(plugin: &PluginSummary) -> bool {    plugin.installed && plugin.enabled && plugin.availability != PluginAvailability::DisabledByAdmin}fn plugin_mention_from_summary(    marketplace_name: &str,    plugin: PluginSummary,) -> Option<PluginCapabilitySummary> {    if !plugin_is_eligible_for_mentions(&plugin) {        return None;    }    Some(PluginCapabilitySummary {        config_name: plugin.id.clone(),        display_name: plugin_mention_display_name(&plugin),        description: plugin_mention_description(marketplace_name, &plugin),        has_skills: false,        mcp_server_names: Vec::new(),        app_connector_ids: Vec::new(),    })}fn plugin_mention_display_name(plugin: &PluginSummary) -> String {    plugin        .interface        .as_ref()        .and_then(|interface| interface.display_name.as_deref())        .map(str::trim)        .filter(|display_name| !display_name.is_empty())        .map(str::to_string)        .unwrap_or_else(|| plugin.name.clone())}fn plugin_mention_description(marketplace_name: &str, plugin: &PluginSummary) -> Option<String> {    plugin        .interface        .as_ref()        .and_then(|interface| interface.short_description.as_deref())        .map(str::trim)        .filter(|description| !description.is_empty())        .map(str::to_string)        .or_else(|| {            let marketplace_name = marketplace_name.trim();            (!marketplace_name.is_empty()).then(|| marketplace_name.to_string())        })}#[cfg(test)]mod tests {    use super::*;    use codex_app_server_protocol::PluginAuthPolicy;    use codex_app_server_protocol::PluginAvailability;    use codex_app_server_protocol::PluginInstallPolicy;    use codex_app_server_protocol::PluginListResponse;    use codex_app_server_protocol::PluginMarketplaceEntry;    use codex_app_server_protocol::PluginSource;    use pretty_assertions::assert_eq;    #[test]    fn plugin_mentions_use_plugin_list_summaries_and_gui_eligibility() {        let active = plugin_summary("active");        let mut disabled_by_admin = plugin_summary("disabled-by-admin");        disabled_by_admin.availability = PluginAvailability::DisabledByAdmin;        let mut disabled = plugin_summary("disabled");        disabled.enabled = false;        let mut uninstalled = plugin_summary("uninstalled");        uninstalled.installed = false;        let response = PluginListResponse {            marketplaces: vec![PluginMarketplaceEntry {                name: "server-marketplace".to_string(),                path: None,                interface: None,                plugins: vec![active, disabled_by_admin, disabled, uninstalled],            }],            marketplace_load_errors: Vec::new(),            featured_plugin_ids: Vec::new(),        };        assert_eq!(            plugin_mentions_from_list_response(response),            vec![PluginCapabilitySummary {                config_name: "active@server-marketplace".to_string(),                display_name: "active".to_string(),                description: Some("server-marketplace".to_string()),                has_skills: false,                mcp_server_names: Vec::new(),                app_connector_ids: Vec::new(),            }]        );    }    fn plugin_summary(name: &str) -> PluginSummary {        PluginSummary {            id: format!("{name}@server-marketplace"),            remote_plugin_id: Some(format!("plugins~{name}")),            local_version: None,            name: name.to_string(),            share_context: None,            source: PluginSource::Remote,            installed: true,            enabled: true,            install_policy: PluginInstallPolicy::Available,            auth_policy: PluginAuthPolicy::OnInstall,            availability: PluginAvailability::Available,            interface: None,            keywords: Vec::new(),        }    }}