use crate::commands::{
BuiltInCommand, CommandRegistry, FastModeCommand, ParsedSlashCommand, parse_fast_mode_command,
parse_slash_command,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(in crate::tui) enum SubmitDecision {
Accept,
IgnoreEmpty,
QueueSteering,
}
#[derive(Debug, PartialEq, Eq)]
pub(in crate::tui) enum TuiSubmitCommand<'a> {
Compact(Option<&'a str>),
Changes,
Export(Option<&'a str>),
Rewind(Option<&'a str>),
Help,
Update(Option<&'a str>),
Login(Option<&'a str>),
Logout(Option<&'a str>),
Model(Option<&'a str>),
Models(Option<&'a str>),
Usage(Option<&'a str>),
Mcp(Option<&'a str>),
Tools(Option<&'a str>),
Subagents(Option<&'a str>),
Skills,
Sessions(Option<&'a str>),
Theme(Option<&'a str>),
SystemPrompt,
New,
PruneSessions(Option<&'a str>),
Fast(FastModeCommand),
FastUsage,
Summarize { enabled: bool, arg: Option<&'a str> },
Quit,
Unsupported(&'a str),
None,
}
impl TuiSubmitCommand<'_> {
pub(in crate::tui) fn requires_idle(&self) -> bool {
matches!(
self,
Self::Compact(_)
| Self::Update(_)
| Self::Export(_)
| Self::Rewind(_)
| Self::Login(_)
| Self::Logout(_)
| Self::Model(_)
| Self::Models(_)
| Self::Usage(_)
| Self::Mcp(_)
| Self::Tools(_)
| Self::Subagents(_)
| Self::Skills
| Self::Sessions(_)
| Self::Theme(_)
| Self::PruneSessions(_)
| Self::New
| Self::Fast(FastModeCommand::Toggle | FastModeCommand::On | FastModeCommand::Off)
)
}
pub(in crate::tui) fn active_run_error(&self) -> Option<&'static str> {
match self {
Self::Compact(_) => Some("cannot compact while a prompt is running"),
Self::Update(_) => Some("cannot update while a prompt is running"),
Self::Export(_) => Some("cannot export while a prompt is running"),
Self::Rewind(_) => Some("cannot rewind while a prompt is running"),
Self::Login(_) => Some("cannot start login while a prompt is running"),
Self::Logout(_) => Some("cannot logout while a prompt is running"),
Self::Model(_) => Some("cannot change model while a prompt is running"),
Self::Models(_) => Some("cannot manage models while a prompt is running"),
Self::Usage(_) => Some("wait for the active run to finish before opening usage"),
Self::Mcp(_) => Some("cannot manage MCP servers while a prompt is running"),
Self::Tools(_) => Some("cannot manage tools while a prompt is running"),
Self::Subagents(_) => Some("cannot manage subagents while a prompt is running"),
Self::Skills => Some("cannot manage skills while a prompt is running"),
Self::Sessions(_) => Some("cannot switch sessions while a prompt is running"),
Self::Theme(_) => Some("cannot change theme while a prompt is running"),
Self::PruneSessions(_) => Some("cannot prune sessions while a prompt is running"),
Self::New => Some("cannot start a new session while a prompt is running"),
Self::Fast(FastModeCommand::Toggle | FastModeCommand::On | FastModeCommand::Off) => {
Some("cannot change fast mode while a prompt is running")
}
Self::Fast(FastModeCommand::Status) | Self::FastUsage => None,
Self::SystemPrompt
| Self::Summarize { .. }
| Self::Changes
| Self::Help
| Self::Quit
| Self::Unsupported(_)
| Self::None => None,
}
}
}
pub(in crate::tui) fn tui_submit_command<'a>(
prompt: &'a str,
commands: &CommandRegistry,
) -> TuiSubmitCommand<'a> {
match parse_slash_command(prompt, commands) {
ParsedSlashCommand::Empty | ParsedSlashCommand::Prompt(_) => TuiSubmitCommand::None,
ParsedSlashCommand::Unknown { token } => TuiSubmitCommand::Unsupported(token),
ParsedSlashCommand::BuiltIn { command, arg } => match command {
BuiltInCommand::Help => TuiSubmitCommand::Help,
BuiltInCommand::Update => TuiSubmitCommand::Update(arg),
BuiltInCommand::Login => TuiSubmitCommand::Login(arg),
BuiltInCommand::Logout => TuiSubmitCommand::Logout(arg),
BuiltInCommand::Compact => TuiSubmitCommand::Compact(arg),
BuiltInCommand::SummarizeStart => TuiSubmitCommand::Summarize { enabled: true, arg },
BuiltInCommand::SummarizeStop => TuiSubmitCommand::Summarize {
enabled: false,
arg,
},
BuiltInCommand::Fast => parse_fast_mode_command(arg)
.map(TuiSubmitCommand::Fast)
.unwrap_or(TuiSubmitCommand::FastUsage),
BuiltInCommand::Theme => TuiSubmitCommand::Theme(arg),
BuiltInCommand::Changes => TuiSubmitCommand::Changes,
BuiltInCommand::Export => TuiSubmitCommand::Export(arg),
BuiltInCommand::Rewind => TuiSubmitCommand::Rewind(arg),
BuiltInCommand::New => TuiSubmitCommand::New,
BuiltInCommand::Model => TuiSubmitCommand::Model(arg),
BuiltInCommand::Models => TuiSubmitCommand::Models(arg),
BuiltInCommand::Usage => TuiSubmitCommand::Usage(arg),
BuiltInCommand::Mcp => TuiSubmitCommand::Mcp(arg),
BuiltInCommand::Tools => TuiSubmitCommand::Tools(arg),
BuiltInCommand::Subagents => TuiSubmitCommand::Subagents(arg),
BuiltInCommand::SystemPrompt => TuiSubmitCommand::SystemPrompt,
BuiltInCommand::Skills => TuiSubmitCommand::Skills,
BuiltInCommand::Sessions => TuiSubmitCommand::Sessions(arg),
BuiltInCommand::PruneSessions => TuiSubmitCommand::PruneSessions(arg),
BuiltInCommand::Quit => TuiSubmitCommand::Quit,
},
}
}
pub(in crate::tui) fn submit_decision(active_run: bool, prompt: &str) -> SubmitDecision {
if prompt.trim().is_empty() {
SubmitDecision::IgnoreEmpty
} else if active_run {
SubmitDecision::QueueSteering
} else {
SubmitDecision::Accept
}
}
#[cfg(test)]
pub(in crate::tui) fn slash_command_token(prompt: &str) -> Option<&str> {
match parse_slash_command(prompt, &CommandRegistry::mvp()) {
ParsedSlashCommand::BuiltIn { command, .. } => Some(match command {
BuiltInCommand::Help => "/help",
BuiltInCommand::Update => "/update",
BuiltInCommand::Login => "/login",
BuiltInCommand::Logout => "/logout",
BuiltInCommand::Compact => "/compact",
BuiltInCommand::Fast => "/fast",
BuiltInCommand::SummarizeStart => "/summarize-start",
BuiltInCommand::SummarizeStop => "/summarize-stop",
BuiltInCommand::Theme => "/theme",
BuiltInCommand::Changes => "/changes",
BuiltInCommand::Export => "/export",
BuiltInCommand::Rewind => "/rewind",
BuiltInCommand::New => "/new",
BuiltInCommand::Model => "/setmodel",
BuiltInCommand::Models => "/models",
BuiltInCommand::Usage => "/usage",
BuiltInCommand::Mcp => "/mcp",
BuiltInCommand::Tools => "/tools",
BuiltInCommand::Subagents => "/subagents",
BuiltInCommand::SystemPrompt => "/system-prompt",
BuiltInCommand::Skills => "/skills",
BuiltInCommand::Sessions => "/sessions",
BuiltInCommand::PruneSessions => "/prune-sessions",
BuiltInCommand::Quit => "/quit",
}),
ParsedSlashCommand::Unknown { token } => Some(token),
ParsedSlashCommand::Empty | ParsedSlashCommand::Prompt(_) => None,
}
}
#[cfg(test)]
pub(in crate::tui) fn is_new_session_command(prompt: &str) -> bool {
matches!(
tui_submit_command(prompt, &CommandRegistry::mvp()),
TuiSubmitCommand::New
)
}
#[cfg(test)]
pub(in crate::tui) fn is_quit_command(prompt: &str) -> bool {
matches!(
tui_submit_command(prompt, &CommandRegistry::mvp()),
TuiSubmitCommand::Quit
)
}
#[cfg(test)]
pub(in crate::tui) fn unsupported_slash_command(prompt: &str) -> Option<&str> {
match tui_submit_command(prompt, &CommandRegistry::mvp()) {
TuiSubmitCommand::Unsupported(command) => Some(command),
_ => None,
}
}