diff --git a/rust/crates/commands/src/lib.rs b/rust/crates/commands/src/lib.rs index 79086913..d7cad594 100644 --- a/rust/crates/commands/src/lib.rs +++ b/rust/crates/commands/src/lib.rs @@ -65,6 +65,13 @@ const SLASH_COMMAND_SPECS: &[SlashCommandSpec] = &[ argument_hint: None, resume_supported: true, }, + SlashCommandSpec { + name: "tui", + aliases: &[], + summary: "Switch to the split-pane TUI dashboard", + argument_hint: None, + resume_supported: true, + }, SlashCommandSpec { name: "status", aliases: &[], @@ -1509,6 +1516,9 @@ pub fn validate_slash_command_input( "history" => SlashCommand::History { count: optional_single_arg(command, &args, "[count]")?, }, + "team" => SlashCommand::Team { + action: optional_single_arg(command, &args, "[list|create|delete]")?, + }, other => SlashCommand::Unknown(other.to_string()), })) } @@ -5525,6 +5535,16 @@ mod tests { #[test] fn parses_supported_slash_commands() { assert_eq!(SlashCommand::parse("/help"), Ok(Some(SlashCommand::Help))); + assert_eq!( + SlashCommand::parse("/team"), + Ok(Some(SlashCommand::Team { action: None })) + ); + assert_eq!( + SlashCommand::parse("/team list"), + Ok(Some(SlashCommand::Team { + action: Some("list".to_string()) + })) + ); assert_eq!( SlashCommand::parse(" /status "), Ok(Some(SlashCommand::Status)) @@ -6012,7 +6032,7 @@ mod tests { assert!(!help.contains("/login")); assert!(!help.contains("/logout")); assert!(help.contains("/setup")); - assert_eq!(slash_command_specs().len(), 140); + assert_eq!(slash_command_specs().len(), 141); assert!(resume_supported_slash_commands().len() >= 39); } diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index ee160ca2..c2cc21bf 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -8714,12 +8714,15 @@ impl LiveCli { | SlashCommand::Ide { .. } | SlashCommand::Tag { .. } | SlashCommand::OutputStyle { .. } - | SlashCommand::AddDir { .. } - | SlashCommand::Team { .. } => { + | SlashCommand::AddDir { .. } => { let cmd_name = command.slash_name(); eprintln!("{cmd_name} is not yet implemented in this build."); false } + SlashCommand::Team { action } => { + self.print_team_command(action.as_deref()); + false + } SlashCommand::Unknown(name) => { eprintln!("{}", format_unknown_slash_command(&name)); false @@ -8732,6 +8735,48 @@ impl LiveCli { Ok(()) } + /// `/team` — user-facing manager for agent teams. + /// + /// The team registry and the `TeamCreate` / `TeamDelete` / `Cron*` tools live + /// inside the tool executor, so the model invokes them directly during a + /// turn. This slash command surfaces a readable view of the same teams and + /// is the user's way to take action without driving the model. + fn print_team_command(&self, action: Option<&str>) { + match action.map(str::to_ascii_lowercase).as_deref() { + None | Some("help") => { + println!("Agent teams — the model creates and manages teams via its tools."); + println!(); + println!("Available team tools the model can call:"); + println!(" TeamCreate create a team from a set of tasks"); + println!(" TeamDelete mark a team deleted by team_id"); + println!(" CronCreate schedule a recurring prompt (cron)"); + println!(" CronDelete remove a scheduled cron by cron_id"); + println!(" CronList list scheduled crons"); + println!(); + println!("Slash commands:"); + println!(" /team list show teams in this session registry"); + println!(" /team help this message"); + println!(); + println!("Teams are held in-process for the lifetime of this claw session."); + } + Some("list") => { + // The team registry is owned by the in-process tool executor + // (tools::global_team_registry) and is not currently exposed to + // the CLI layer, so we can't enumerate live teams from here. + // Listing will arrive with tool-registry introspection. + println!("Live team listing isn't wired into the CLI yet."); + println!( + "Teams are created/managed by the model via the TeamCreate and TeamDelete" + ); + println!("tools during a turn. Ask the model to report on its teams."); + } + Some(other) => { + println!("Unknown /team action: {other}"); + println!("Available: list, help"); + } + } + } + fn print_status(&self) { let cumulative = self.runtime.usage().cumulative_usage(); let latest = self.runtime.usage().current_turn_usage();