Codex Handbook
tui/src/bottom_pane/slash_commands.rs 347 lines
//! Shared helpers for filtering and matching built-in and model service-tier slash commands.//!//! The same sandbox- and feature-gating rules are used by both the composer//! and the command popup. Centralizing them here keeps those call sites small//! and ensures they stay in sync.use std::str::FromStr;use codex_utils_fuzzy_match::fuzzy_match;use crate::slash_command::SlashCommand;use crate::slash_command::built_in_slash_commands;#[derive(Clone, Debug, PartialEq, Eq)]pub(crate) struct ServiceTierCommand {    pub(crate) id: String,    pub(crate) name: String,    pub(crate) description: String,}#[derive(Clone, Debug, PartialEq, Eq)]pub(crate) enum SlashCommandItem {    Builtin(SlashCommand),    ServiceTier(ServiceTierCommand),}impl SlashCommandItem {    pub(crate) fn command(&self) -> &str {        match self {            Self::Builtin(cmd) => cmd.command(),            Self::ServiceTier(command) => &command.name,        }    }    pub(crate) fn supports_inline_args(&self) -> bool {        match self {            Self::Builtin(cmd) => cmd.supports_inline_args(),            Self::ServiceTier(_) => false,        }    }    pub(crate) fn available_in_side_conversation(&self) -> bool {        match self {            Self::Builtin(cmd) => cmd.available_in_side_conversation(),            Self::ServiceTier(_) => false,        }    }    pub(crate) fn available_during_task(&self) -> bool {        match self {            Self::Builtin(cmd) => cmd.available_during_task(),            Self::ServiceTier(_) => false,        }    }}#[derive(Clone, Copy, Debug, Default)]pub(crate) struct BuiltinCommandFlags {    pub(crate) collaboration_modes_enabled: bool,    pub(crate) connectors_enabled: bool,    pub(crate) plugins_command_enabled: bool,    pub(crate) token_activity_command_enabled: bool,    pub(crate) service_tier_commands_enabled: bool,    pub(crate) goal_command_enabled: bool,    pub(crate) personality_command_enabled: bool,    pub(crate) allow_elevate_sandbox: bool,    pub(crate) side_conversation_active: bool,}/// Return the built-ins that should be visible/usable for the current input.pub(crate) fn builtins_for_input(flags: BuiltinCommandFlags) -> Vec<(&'static str, SlashCommand)> {    built_in_slash_commands()        .into_iter()        .filter(|(_, cmd)| flags.allow_elevate_sandbox || *cmd != SlashCommand::ElevateSandbox)        .filter(|(_, cmd)| flags.collaboration_modes_enabled || *cmd != SlashCommand::Plan)        .filter(|(_, cmd)| flags.connectors_enabled || *cmd != SlashCommand::Apps)        .filter(|(_, cmd)| flags.plugins_command_enabled || *cmd != SlashCommand::Plugins)        .filter(|(_, cmd)| flags.token_activity_command_enabled || *cmd != SlashCommand::Usage)        .filter(|(_, cmd)| flags.goal_command_enabled || *cmd != SlashCommand::Goal)        .filter(|(_, cmd)| flags.personality_command_enabled || *cmd != SlashCommand::Personality)        .filter(|(_, cmd)| !flags.side_conversation_active || cmd.available_in_side_conversation())        .collect()}pub(crate) fn commands_for_input(    flags: BuiltinCommandFlags,    service_tier_commands: &[ServiceTierCommand],) -> Vec<SlashCommandItem> {    let mut commands = Vec::new();    let tiers_enabled = flags.service_tier_commands_enabled;    for (_, cmd) in builtins_for_input(flags) {        commands.push(SlashCommandItem::Builtin(cmd));        if cmd == SlashCommand::Model && tiers_enabled {            commands.extend(                service_tier_commands                    .iter()                    .cloned()                    .map(SlashCommandItem::ServiceTier),            );        }    }    commands        .into_iter()        .filter(|cmd| !flags.side_conversation_active || cmd.available_in_side_conversation())        .collect()}/// Find a single built-in command by a recognized name or alias, after applying feature gating.////// Side-conversation and token-activity gating are intentionally enforced by dispatch rather than/// command lookup so a typed command can produce a specific unavailable message while the popup/// still hides it.pub(crate) fn find_builtin_command(name: &str, flags: BuiltinCommandFlags) -> Option<SlashCommand> {    let cmd = SlashCommand::from_str(name).ok().or_else(|| {        let repeated_os = name.strip_prefix('g')?.strip_suffix("al")?;        (!repeated_os.is_empty() && repeated_os.bytes().all(|byte| byte == b'o'))            .then_some(SlashCommand::Goal)    })?;    builtins_for_input(BuiltinCommandFlags {        token_activity_command_enabled: true,        side_conversation_active: false,        ..flags    })    .into_iter()    .any(|(_, visible_cmd)| visible_cmd == cmd)    .then_some(cmd)}pub(crate) fn find_slash_command(    name: &str,    flags: BuiltinCommandFlags,    service_tier_commands: &[ServiceTierCommand],) -> Option<SlashCommandItem> {    if let Some(cmd) = find_builtin_command(name, flags) {        return Some(SlashCommandItem::Builtin(cmd));    }    let tiers_enabled = flags.service_tier_commands_enabled;    tiers_enabled        .then(|| {            service_tier_commands                .iter()                .find(|command| command.name == name)                .cloned()                .map(SlashCommandItem::ServiceTier)        })        .flatten()}pub(crate) fn has_slash_command_prefix(    name: &str,    flags: BuiltinCommandFlags,    service_tier_commands: &[ServiceTierCommand],) -> bool {    commands_for_input(flags, service_tier_commands)        .into_iter()        .any(|command| fuzzy_match(command.command(), name).is_some())}#[cfg(test)]mod tests {    use super::*;    use pretty_assertions::assert_eq;    use std::slice::from_ref;    fn all_enabled_flags() -> BuiltinCommandFlags {        BuiltinCommandFlags {            collaboration_modes_enabled: true,            connectors_enabled: true,            plugins_command_enabled: true,            token_activity_command_enabled: true,            service_tier_commands_enabled: true,            goal_command_enabled: true,            personality_command_enabled: true,            allow_elevate_sandbox: true,            side_conversation_active: false,        }    }    #[test]    fn debug_command_still_resolves_for_dispatch() {        let cmd = find_builtin_command("debug-config", all_enabled_flags());        assert_eq!(cmd, Some(SlashCommand::DebugConfig));    }    #[test]    fn clear_command_resolves_for_dispatch() {        assert_eq!(            find_builtin_command("clear", all_enabled_flags()),            Some(SlashCommand::Clear)        );    }    #[test]    fn goal_command_allows_extra_os_for_dispatch() {        assert_eq!(            find_builtin_command("goooooooooooal", all_enabled_flags()),            Some(SlashCommand::Goal)        );    }    #[test]    fn stop_command_resolves_for_dispatch() {        assert_eq!(            find_builtin_command("stop", all_enabled_flags()),            Some(SlashCommand::Stop)        );    }    #[test]    fn clean_command_alias_resolves_for_dispatch() {        assert_eq!(            find_builtin_command("clean", all_enabled_flags()),            Some(SlashCommand::Stop)        );    }    #[test]    fn service_tier_commands_are_hidden_when_disabled() {        let mut flags = all_enabled_flags();        flags.service_tier_commands_enabled = false;        let commands = vec![ServiceTierCommand {            id: "priority".to_string(),            name: "fast".to_string(),            description: "fastest inference".to_string(),        }];        assert_eq!(find_slash_command("fast", flags, &commands), None);    }    #[test]    fn all_service_tiers_are_exposed_as_commands_after_model() {        let commands = vec![            ServiceTierCommand {                id: "priority".to_string(),                name: "fast".to_string(),                description: "fastest inference".to_string(),            },            ServiceTierCommand {                id: "batch".to_string(),                name: "slow".to_string(),                description: "slower inference with lower priority".to_string(),            },        ];        let items = commands_for_input(all_enabled_flags(), &commands);        let model_idx = items            .iter()            .position(|item| matches!(item, SlashCommandItem::Builtin(SlashCommand::Model)))            .expect("model command should be visible");        let inserted = items            .into_iter()            .skip(model_idx + 1)            .take(commands.len())            .collect::<Vec<_>>();        let expected = commands            .into_iter()            .map(SlashCommandItem::ServiceTier)            .collect::<Vec<_>>();        assert_eq!(inserted, expected);    }    #[test]    fn goal_command_is_hidden_when_disabled() {        let mut flags = all_enabled_flags();        flags.goal_command_enabled = false;        assert_eq!(find_builtin_command("goal", flags), None);    }    #[test]    fn usage_command_is_hidden_from_input_when_account_token_activity_is_disabled() {        let mut flags = all_enabled_flags();        flags.token_activity_command_enabled = false;        assert_eq!(            builtins_for_input(flags)                .into_iter()                .find(|(_, command)| *command == SlashCommand::Usage),            None        );    }    #[test]    fn usage_command_exact_lookup_still_resolves_when_account_token_activity_is_disabled() {        let mut flags = all_enabled_flags();        flags.token_activity_command_enabled = false;        assert_eq!(            find_builtin_command("usage", flags),            Some(SlashCommand::Usage)        );    }    #[test]    fn side_conversation_hides_commands_without_side_flag() {        let commands = builtins_for_input(BuiltinCommandFlags {            side_conversation_active: true,            ..all_enabled_flags()        })        .into_iter()        .map(|(_, command)| command)        .collect::<Vec<_>>();        assert_eq!(            commands,            vec![                SlashCommand::Ide,                SlashCommand::Copy,                SlashCommand::Raw,                SlashCommand::Diff,                SlashCommand::Mention,                SlashCommand::Status,                SlashCommand::Usage,            ]        );    }    #[test]    fn side_conversation_exact_lookup_still_resolves_hidden_commands_for_dispatch_error() {        assert_eq!(            find_builtin_command(                "review",                BuiltinCommandFlags {                    side_conversation_active: true,                    ..all_enabled_flags()                },            ),            Some(SlashCommand::Review)        );    }    #[test]    fn side_conversation_exact_lookup_still_resolves_service_tier_commands_for_dispatch_error() {        let command = ServiceTierCommand {            id: "priority".to_string(),            name: "fast".to_string(),            description: "fastest inference".to_string(),        };        let flags = BuiltinCommandFlags {            side_conversation_active: true,            ..all_enabled_flags()        };        assert_eq!(            find_slash_command("fast", flags, from_ref(&command)),            Some(SlashCommandItem::ServiceTier(command))        );    }}