From c17a609cb2f92a3eedcef501a72d6e20ffc8a94b Mon Sep 17 00:00:00 2001 From: Charles Cunningham Date: Sat, 21 Mar 2026 01:06:02 -0700 Subject: [PATCH] tui: use generic slash parser building blocks Replace the remaining parse-kind special cases with declarative slash parser primitives so each command spec owns its inline-arg parser as NoArgs, Remainder, or token choices in both tui implementations. Co-authored-by: Codex --- codex-rs/tui/src/chatwidget.rs | 4 +- codex-rs/tui/src/slash_command.rs | 154 ++++++++++++------ codex-rs/tui/src/slash_command_input.rs | 52 +----- codex-rs/tui_app_server/src/chatwidget.rs | 4 +- codex-rs/tui_app_server/src/slash_command.rs | 152 +++++++++++------ .../tui_app_server/src/slash_command_input.rs | 52 +----- 6 files changed, 233 insertions(+), 185 deletions(-) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 27c0abf397..3592d16651 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -271,9 +271,9 @@ use crate::render::renderable::FlexRenderable; use crate::render::renderable::Renderable; use crate::render::renderable::RenderableExt; use crate::render::renderable::RenderableItem; +use crate::slash_command::FastSlashCommandArgs; +use crate::slash_command::ParsedSlashCommand; use crate::slash_command::SlashCommand; -use crate::slash_command_input::FastSlashCommandArgs; -use crate::slash_command_input::ParsedSlashCommand; use crate::status::RateLimitSnapshotDisplay; use crate::status_indicator_widget::STATUS_DETAILS_DEFAULT_MAX_LINES; use crate::status_indicator_widget::StatusDetailsCapitalization; diff --git a/codex-rs/tui/src/slash_command.rs b/codex-rs/tui/src/slash_command.rs index 44443809b9..fbcf68c1f7 100644 --- a/codex-rs/tui/src/slash_command.rs +++ b/codex-rs/tui/src/slash_command.rs @@ -73,15 +73,73 @@ pub(crate) enum SlashCommandBareBehavior { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum SlashCommandParseKind { - NoArgs, - Fast, +pub(crate) enum FastSlashCommandArgs { + On, + Off, + Status, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ParsedSlashCommand { + Bare(SlashCommandBareBehavior), + Fast(FastSlashCommandArgs), Rename, Plan, Review, SandboxReadRoot, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum SlashCommandUsageErrorKind { + UnexpectedInlineArgs, + InvalidInlineArgs, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum SlashCommandArgsParser { + NoArgs, + Remainder(ParsedSlashCommand), + Tokens(&'static [SlashCommandTokenChoice]), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct SlashCommandTokenChoice { + pub(crate) token: &'static str, + pub(crate) parsed: ParsedSlashCommand, +} + +const FAST_TOKEN_CHOICES: &[SlashCommandTokenChoice] = &[ + SlashCommandTokenChoice { + token: "on", + parsed: ParsedSlashCommand::Fast(FastSlashCommandArgs::On), + }, + SlashCommandTokenChoice { + token: "off", + parsed: ParsedSlashCommand::Fast(FastSlashCommandArgs::Off), + }, + SlashCommandTokenChoice { + token: "status", + parsed: ParsedSlashCommand::Fast(FastSlashCommandArgs::Status), + }, +]; + +impl SlashCommandArgsParser { + pub(crate) fn parse( + self, + args: &str, + ) -> Result { + match self { + SlashCommandArgsParser::NoArgs => Err(SlashCommandUsageErrorKind::UnexpectedInlineArgs), + SlashCommandArgsParser::Remainder(parsed) => Ok(parsed), + SlashCommandArgsParser::Tokens(choices) => choices + .iter() + .find(|choice| choice.token.eq_ignore_ascii_case(args)) + .map(|choice| choice.parsed) + .ok_or(SlashCommandUsageErrorKind::InvalidInlineArgs), + } + } +} + impl SlashCommand { pub(crate) fn spec(self) -> SlashCommandSpec { match self { @@ -92,7 +150,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/model"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Fast => SlashCommandSpec { description: "toggle Fast mode to enable fastest inference at 2X plan usage", @@ -101,7 +159,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/fast", "/fast [on|off|status]"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::Fast, + args_parser: SlashCommandArgsParser::Tokens(FAST_TOKEN_CHOICES), }, SlashCommand::Approvals => SlashCommandSpec { description: "choose what Codex is allowed to do", @@ -110,7 +168,7 @@ impl SlashCommand { hide_in_command_popup: true, usage_lines: &["/approvals"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Permissions => SlashCommandSpec { description: "choose what Codex is allowed to do", @@ -119,7 +177,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/permissions"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::ElevateSandbox => SlashCommandSpec { description: "set up elevated agent sandbox", @@ -128,7 +186,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/setup-default-sandbox"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::SandboxReadRoot => SlashCommandSpec { description: "let sandbox read a directory: /sandbox-add-read-dir ", @@ -137,7 +195,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/sandbox-add-read-dir "], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::SandboxReadRoot, + args_parser: SlashCommandArgsParser::Remainder(ParsedSlashCommand::SandboxReadRoot), }, SlashCommand::Experimental => SlashCommandSpec { description: "toggle experimental features", @@ -146,7 +204,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/experimental"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Skills => SlashCommandSpec { description: "use skills to improve how Codex performs specific tasks", @@ -155,7 +213,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/skills"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Review => SlashCommandSpec { description: "review my current changes and find issues", @@ -164,7 +222,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/review", "/review "], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::Review, + args_parser: SlashCommandArgsParser::Remainder(ParsedSlashCommand::Review), }, SlashCommand::Rename => SlashCommandSpec { description: "rename the current thread", @@ -173,7 +231,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/rename", "/rename "], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::Rename, + args_parser: SlashCommandArgsParser::Remainder(ParsedSlashCommand::Rename), }, SlashCommand::New => SlashCommandSpec { description: "start a new chat during a conversation", @@ -182,7 +240,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/new"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Resume => SlashCommandSpec { description: "resume a saved chat", @@ -191,7 +249,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/resume"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Fork => SlashCommandSpec { description: "fork the current chat", @@ -200,7 +258,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/fork"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Init => SlashCommandSpec { description: "create an AGENTS.md file with instructions for Codex", @@ -209,7 +267,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/init"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Compact => SlashCommandSpec { description: "summarize conversation to prevent hitting the context limit", @@ -218,7 +276,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/compact"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Plan => SlashCommandSpec { description: "switch to Plan mode", @@ -227,7 +285,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/plan", "/plan <prompt>"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::Plan, + args_parser: SlashCommandArgsParser::Remainder(ParsedSlashCommand::Plan), }, SlashCommand::Collab => SlashCommandSpec { description: "change collaboration mode (experimental)", @@ -236,7 +294,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/collab"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Agent => SlashCommandSpec { description: "switch the active agent thread", @@ -245,7 +303,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/agent"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Diff => SlashCommandSpec { description: "show git diff (including untracked files)", @@ -254,7 +312,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/diff"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Copy => SlashCommandSpec { description: "copy the latest Codex output to your clipboard", @@ -263,7 +321,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/copy"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Mention => SlashCommandSpec { description: "mention a file", @@ -272,7 +330,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/mention"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Status => SlashCommandSpec { description: "show current session configuration and token usage", @@ -281,7 +339,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/status"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::DebugConfig => SlashCommandSpec { description: "show config layers and requirement sources for debugging", @@ -290,7 +348,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/debug-config"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Title => SlashCommandSpec { description: "configure which items appear in the terminal title", @@ -299,7 +357,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/title"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Statusline => SlashCommandSpec { description: "configure which items appear in the status line", @@ -308,7 +366,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/statusline"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Theme => SlashCommandSpec { description: "choose a syntax highlighting theme", @@ -317,7 +375,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/theme"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Mcp => SlashCommandSpec { description: "list configured MCP tools", @@ -326,7 +384,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/mcp"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Apps => SlashCommandSpec { description: "manage apps", @@ -335,7 +393,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/apps"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Plugins => SlashCommandSpec { description: "browse plugins", @@ -344,7 +402,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/plugins"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Logout => SlashCommandSpec { description: "log out of Codex", @@ -353,7 +411,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/logout"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Quit => SlashCommandSpec { description: "exit Codex", @@ -362,7 +420,7 @@ impl SlashCommand { hide_in_command_popup: true, usage_lines: &["/quit"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Exit => SlashCommandSpec { description: "exit Codex", @@ -371,7 +429,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/exit"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Feedback => SlashCommandSpec { description: "send logs to maintainers", @@ -380,7 +438,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/feedback"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Rollout => SlashCommandSpec { description: "print the rollout file path", @@ -389,7 +447,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/rollout"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Ps => SlashCommandSpec { description: "list background terminals", @@ -398,7 +456,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/ps"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Stop => SlashCommandSpec { description: "stop all background terminals", @@ -407,7 +465,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/stop"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Clear => SlashCommandSpec { description: "clear the terminal and start a new chat", @@ -416,7 +474,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/clear"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Personality => SlashCommandSpec { description: "choose a communication style for Codex", @@ -425,7 +483,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/personality"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Realtime => SlashCommandSpec { description: "toggle realtime voice mode (experimental)", @@ -434,7 +492,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/realtime"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Settings => SlashCommandSpec { description: "configure realtime microphone/speaker", @@ -443,7 +501,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/settings"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::TestApproval => SlashCommandSpec { description: "test approval request", @@ -452,7 +510,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/test-approval"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::MultiAgents => SlashCommandSpec { description: "switch the active agent thread", @@ -461,7 +519,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/subagents"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::MemoryDrop => SlashCommandSpec { description: "DO NOT USE", @@ -470,7 +528,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/debug-m-drop"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::MemoryUpdate => SlashCommandSpec { description: "DO NOT USE", @@ -479,7 +537,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/debug-m-update"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, } } @@ -533,7 +591,7 @@ pub(crate) struct SlashCommandSpec { pub(crate) hide_in_command_popup: bool, pub(crate) usage_lines: &'static [&'static str], pub(crate) bare_behavior: SlashCommandBareBehavior, - pub(crate) parse_kind: SlashCommandParseKind, + pub(crate) args_parser: SlashCommandArgsParser, } #[cfg(test)] diff --git a/codex-rs/tui/src/slash_command_input.rs b/codex-rs/tui/src/slash_command_input.rs index b288517c42..81b560c593 100644 --- a/codex-rs/tui/src/slash_command_input.rs +++ b/codex-rs/tui/src/slash_command_input.rs @@ -1,29 +1,6 @@ +use crate::slash_command::ParsedSlashCommand; use crate::slash_command::SlashCommand; -use crate::slash_command::SlashCommandBareBehavior; -use crate::slash_command::SlashCommandParseKind; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum FastSlashCommandArgs { - On, - Off, - Status, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum ParsedSlashCommand { - Bare(SlashCommandBareBehavior), - Fast(FastSlashCommandArgs), - Rename, - Plan, - Review, - SandboxReadRoot, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum SlashCommandUsageErrorKind { - UnexpectedInlineArgs, - InvalidInlineArgs, -} +use crate::slash_command::SlashCommandUsageErrorKind; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct SlashCommandUsageError { @@ -55,25 +32,12 @@ impl SlashCommand { return Ok(ParsedSlashCommand::Bare(spec.bare_behavior)); } - match spec.parse_kind { - SlashCommandParseKind::NoArgs => Err(SlashCommandUsageError { + spec.args_parser + .parse(trimmed) + .map_err(|kind| SlashCommandUsageError { command: self, - kind: SlashCommandUsageErrorKind::UnexpectedInlineArgs, - }), - SlashCommandParseKind::Fast => match trimmed.to_ascii_lowercase().as_str() { - "on" => Ok(ParsedSlashCommand::Fast(FastSlashCommandArgs::On)), - "off" => Ok(ParsedSlashCommand::Fast(FastSlashCommandArgs::Off)), - "status" => Ok(ParsedSlashCommand::Fast(FastSlashCommandArgs::Status)), - _ => Err(SlashCommandUsageError { - command: self, - kind: SlashCommandUsageErrorKind::InvalidInlineArgs, - }), - }, - SlashCommandParseKind::Rename => Ok(ParsedSlashCommand::Rename), - SlashCommandParseKind::Plan => Ok(ParsedSlashCommand::Plan), - SlashCommandParseKind::Review => Ok(ParsedSlashCommand::Review), - SlashCommandParseKind::SandboxReadRoot => Ok(ParsedSlashCommand::SandboxReadRoot), - } + kind, + }) } } @@ -82,6 +46,8 @@ mod tests { use pretty_assertions::assert_eq; use super::*; + use crate::slash_command::FastSlashCommandArgs; + use crate::slash_command::SlashCommandBareBehavior; #[test] fn review_bare_form_is_marked_as_ui_driven() { diff --git a/codex-rs/tui_app_server/src/chatwidget.rs b/codex-rs/tui_app_server/src/chatwidget.rs index 6e58768b45..687a665bb5 100644 --- a/codex-rs/tui_app_server/src/chatwidget.rs +++ b/codex-rs/tui_app_server/src/chatwidget.rs @@ -315,9 +315,9 @@ use crate::render::renderable::FlexRenderable; use crate::render::renderable::Renderable; use crate::render::renderable::RenderableExt; use crate::render::renderable::RenderableItem; +use crate::slash_command::FastSlashCommandArgs; +use crate::slash_command::ParsedSlashCommand; use crate::slash_command::SlashCommand; -use crate::slash_command_input::FastSlashCommandArgs; -use crate::slash_command_input::ParsedSlashCommand; use crate::status::RateLimitSnapshotDisplay; use crate::status_indicator_widget::STATUS_DETAILS_DEFAULT_MAX_LINES; use crate::status_indicator_widget::StatusDetailsCapitalization; diff --git a/codex-rs/tui_app_server/src/slash_command.rs b/codex-rs/tui_app_server/src/slash_command.rs index 578382296c..d2239f59bb 100644 --- a/codex-rs/tui_app_server/src/slash_command.rs +++ b/codex-rs/tui_app_server/src/slash_command.rs @@ -72,15 +72,73 @@ pub(crate) enum SlashCommandBareBehavior { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum SlashCommandParseKind { - NoArgs, - Fast, +pub(crate) enum FastSlashCommandArgs { + On, + Off, + Status, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ParsedSlashCommand { + Bare(SlashCommandBareBehavior), + Fast(FastSlashCommandArgs), Rename, Plan, Review, SandboxReadRoot, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum SlashCommandUsageErrorKind { + UnexpectedInlineArgs, + InvalidInlineArgs, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum SlashCommandArgsParser { + NoArgs, + Remainder(ParsedSlashCommand), + Tokens(&'static [SlashCommandTokenChoice]), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct SlashCommandTokenChoice { + pub(crate) token: &'static str, + pub(crate) parsed: ParsedSlashCommand, +} + +const FAST_TOKEN_CHOICES: &[SlashCommandTokenChoice] = &[ + SlashCommandTokenChoice { + token: "on", + parsed: ParsedSlashCommand::Fast(FastSlashCommandArgs::On), + }, + SlashCommandTokenChoice { + token: "off", + parsed: ParsedSlashCommand::Fast(FastSlashCommandArgs::Off), + }, + SlashCommandTokenChoice { + token: "status", + parsed: ParsedSlashCommand::Fast(FastSlashCommandArgs::Status), + }, +]; + +impl SlashCommandArgsParser { + pub(crate) fn parse( + self, + args: &str, + ) -> Result<ParsedSlashCommand, SlashCommandUsageErrorKind> { + match self { + SlashCommandArgsParser::NoArgs => Err(SlashCommandUsageErrorKind::UnexpectedInlineArgs), + SlashCommandArgsParser::Remainder(parsed) => Ok(parsed), + SlashCommandArgsParser::Tokens(choices) => choices + .iter() + .find(|choice| choice.token.eq_ignore_ascii_case(args)) + .map(|choice| choice.parsed) + .ok_or(SlashCommandUsageErrorKind::InvalidInlineArgs), + } + } +} + impl SlashCommand { pub(crate) fn spec(self) -> SlashCommandSpec { match self { @@ -91,7 +149,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/model"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Fast => SlashCommandSpec { description: "toggle Fast mode to enable fastest inference at 2X plan usage", @@ -100,7 +158,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/fast", "/fast [on|off|status]"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::Fast, + args_parser: SlashCommandArgsParser::Tokens(FAST_TOKEN_CHOICES), }, SlashCommand::Approvals => SlashCommandSpec { description: "choose what Codex is allowed to do", @@ -109,7 +167,7 @@ impl SlashCommand { hide_in_command_popup: true, usage_lines: &["/approvals"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Permissions => SlashCommandSpec { description: "choose what Codex is allowed to do", @@ -118,7 +176,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/permissions"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::ElevateSandbox => SlashCommandSpec { description: "set up elevated agent sandbox", @@ -127,7 +185,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/setup-default-sandbox"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::SandboxReadRoot => SlashCommandSpec { description: "let sandbox read a directory: /sandbox-add-read-dir <absolute_path>", @@ -136,7 +194,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/sandbox-add-read-dir <absolute-path>"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::SandboxReadRoot, + args_parser: SlashCommandArgsParser::Remainder(ParsedSlashCommand::SandboxReadRoot), }, SlashCommand::Experimental => SlashCommandSpec { description: "toggle experimental features", @@ -145,7 +203,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/experimental"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Skills => SlashCommandSpec { description: "use skills to improve how Codex performs specific tasks", @@ -154,7 +212,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/skills"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Review => SlashCommandSpec { description: "review my current changes and find issues", @@ -163,7 +221,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/review", "/review <instructions>"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::Review, + args_parser: SlashCommandArgsParser::Remainder(ParsedSlashCommand::Review), }, SlashCommand::Rename => SlashCommandSpec { description: "rename the current thread", @@ -172,7 +230,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/rename", "/rename <title>"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::Rename, + args_parser: SlashCommandArgsParser::Remainder(ParsedSlashCommand::Rename), }, SlashCommand::New => SlashCommandSpec { description: "start a new chat during a conversation", @@ -181,7 +239,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/new"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Resume => SlashCommandSpec { description: "resume a saved chat", @@ -190,7 +248,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/resume"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Fork => SlashCommandSpec { description: "fork the current chat", @@ -199,7 +257,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/fork"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Init => SlashCommandSpec { description: "create an AGENTS.md file with instructions for Codex", @@ -208,7 +266,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/init"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Compact => SlashCommandSpec { description: "summarize conversation to prevent hitting the context limit", @@ -217,7 +275,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/compact"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Plan => SlashCommandSpec { description: "switch to Plan mode", @@ -226,7 +284,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/plan", "/plan <prompt>"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::Plan, + args_parser: SlashCommandArgsParser::Remainder(ParsedSlashCommand::Plan), }, SlashCommand::Collab => SlashCommandSpec { description: "change collaboration mode (experimental)", @@ -235,7 +293,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/collab"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Agent => SlashCommandSpec { description: "switch the active agent thread", @@ -244,7 +302,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/agent"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Diff => SlashCommandSpec { description: "show git diff (including untracked files)", @@ -253,7 +311,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/diff"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Copy => SlashCommandSpec { description: "copy the latest Codex output to your clipboard", @@ -262,7 +320,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/copy"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Mention => SlashCommandSpec { description: "mention a file", @@ -271,7 +329,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/mention"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Status => SlashCommandSpec { description: "show current session configuration and token usage", @@ -280,7 +338,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/status"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::DebugConfig => SlashCommandSpec { description: "show config layers and requirement sources for debugging", @@ -289,7 +347,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/debug-config"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Statusline => SlashCommandSpec { description: "configure which items appear in the status line", @@ -298,7 +356,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/statusline"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Theme => SlashCommandSpec { description: "choose a syntax highlighting theme", @@ -307,7 +365,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/theme"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Mcp => SlashCommandSpec { description: "list configured MCP tools", @@ -316,7 +374,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/mcp"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Apps => SlashCommandSpec { description: "manage apps", @@ -325,7 +383,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/apps"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Plugins => SlashCommandSpec { description: "browse plugins", @@ -334,7 +392,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/plugins"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Logout => SlashCommandSpec { description: "log out of Codex", @@ -343,7 +401,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/logout"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Quit => SlashCommandSpec { description: "exit Codex", @@ -352,7 +410,7 @@ impl SlashCommand { hide_in_command_popup: true, usage_lines: &["/quit"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Exit => SlashCommandSpec { description: "exit Codex", @@ -361,7 +419,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/exit"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Feedback => SlashCommandSpec { description: "send logs to maintainers", @@ -370,7 +428,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/feedback"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Rollout => SlashCommandSpec { description: "print the rollout file path", @@ -379,7 +437,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/rollout"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Ps => SlashCommandSpec { description: "list background terminals", @@ -388,7 +446,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/ps"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Stop => SlashCommandSpec { description: "stop all background terminals", @@ -397,7 +455,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/stop"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Clear => SlashCommandSpec { description: "clear the terminal and start a new chat", @@ -406,7 +464,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/clear"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Personality => SlashCommandSpec { description: "choose a communication style for Codex", @@ -415,7 +473,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/personality"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Realtime => SlashCommandSpec { description: "toggle realtime voice mode (experimental)", @@ -424,7 +482,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/realtime"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::Settings => SlashCommandSpec { description: "configure realtime microphone/speaker", @@ -433,7 +491,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/settings"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::TestApproval => SlashCommandSpec { description: "test approval request", @@ -442,7 +500,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/test-approval"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::MultiAgents => SlashCommandSpec { description: "switch the active agent thread", @@ -451,7 +509,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/subagents"], bare_behavior: SlashCommandBareBehavior::OpensUi, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::MemoryDrop => SlashCommandSpec { description: "DO NOT USE", @@ -460,7 +518,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/debug-m-drop"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, SlashCommand::MemoryUpdate => SlashCommandSpec { description: "DO NOT USE", @@ -469,7 +527,7 @@ impl SlashCommand { hide_in_command_popup: false, usage_lines: &["/debug-m-update"], bare_behavior: SlashCommandBareBehavior::DispatchesDirectly, - parse_kind: SlashCommandParseKind::NoArgs, + args_parser: SlashCommandArgsParser::NoArgs, }, } } @@ -523,7 +581,7 @@ pub(crate) struct SlashCommandSpec { pub(crate) hide_in_command_popup: bool, pub(crate) usage_lines: &'static [&'static str], pub(crate) bare_behavior: SlashCommandBareBehavior, - pub(crate) parse_kind: SlashCommandParseKind, + pub(crate) args_parser: SlashCommandArgsParser, } #[cfg(test)] diff --git a/codex-rs/tui_app_server/src/slash_command_input.rs b/codex-rs/tui_app_server/src/slash_command_input.rs index b288517c42..81b560c593 100644 --- a/codex-rs/tui_app_server/src/slash_command_input.rs +++ b/codex-rs/tui_app_server/src/slash_command_input.rs @@ -1,29 +1,6 @@ +use crate::slash_command::ParsedSlashCommand; use crate::slash_command::SlashCommand; -use crate::slash_command::SlashCommandBareBehavior; -use crate::slash_command::SlashCommandParseKind; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum FastSlashCommandArgs { - On, - Off, - Status, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum ParsedSlashCommand { - Bare(SlashCommandBareBehavior), - Fast(FastSlashCommandArgs), - Rename, - Plan, - Review, - SandboxReadRoot, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum SlashCommandUsageErrorKind { - UnexpectedInlineArgs, - InvalidInlineArgs, -} +use crate::slash_command::SlashCommandUsageErrorKind; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct SlashCommandUsageError { @@ -55,25 +32,12 @@ impl SlashCommand { return Ok(ParsedSlashCommand::Bare(spec.bare_behavior)); } - match spec.parse_kind { - SlashCommandParseKind::NoArgs => Err(SlashCommandUsageError { + spec.args_parser + .parse(trimmed) + .map_err(|kind| SlashCommandUsageError { command: self, - kind: SlashCommandUsageErrorKind::UnexpectedInlineArgs, - }), - SlashCommandParseKind::Fast => match trimmed.to_ascii_lowercase().as_str() { - "on" => Ok(ParsedSlashCommand::Fast(FastSlashCommandArgs::On)), - "off" => Ok(ParsedSlashCommand::Fast(FastSlashCommandArgs::Off)), - "status" => Ok(ParsedSlashCommand::Fast(FastSlashCommandArgs::Status)), - _ => Err(SlashCommandUsageError { - command: self, - kind: SlashCommandUsageErrorKind::InvalidInlineArgs, - }), - }, - SlashCommandParseKind::Rename => Ok(ParsedSlashCommand::Rename), - SlashCommandParseKind::Plan => Ok(ParsedSlashCommand::Plan), - SlashCommandParseKind::Review => Ok(ParsedSlashCommand::Review), - SlashCommandParseKind::SandboxReadRoot => Ok(ParsedSlashCommand::SandboxReadRoot), - } + kind, + }) } } @@ -82,6 +46,8 @@ mod tests { use pretty_assertions::assert_eq; use super::*; + use crate::slash_command::FastSlashCommandArgs; + use crate::slash_command::SlashCommandBareBehavior; #[test] fn review_bare_form_is_marked_as_ui_driven() {