Complete agent monitoring and Galaxy Control integration

- expose command-monitor conversations and preserve visible agent transcripts
- add bounded polling and a dedicated shell interrupt tool
- improve direct-provider images, skills, tool history, and usage handling
- package and brand Galaxy Control across releases, installers, persistence, and docs
This commit is contained in:
2026-07-29 15:04:58 -05:00
parent 100f1eff1c
commit dbfa8bcd48
172 changed files with 6357 additions and 3825 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "galaxy_cli"
edition = "2024"
description = "CLI argument parsing for Warp"
description = "CLI argument parsing for Galaxy"
authors.workspace = true
publish.workspace = true
license.workspace = true
+4 -4
View File
@@ -105,10 +105,10 @@ pub struct GlobalOptions {
pub output_format: OutputFormat,
}
/// Normal argument parser for the shared Warp executable across all channels.
/// Normal argument parser for the shared Galaxy executable across all channels.
///
/// Oz commands are subcommands of this parser, so invoking an `oz` symlink does
/// not require a mode flag. Warp Control uses its separate [`local_control::ControlArgs`]
/// not require a mode flag. Galaxy Control uses its separate [`local_control::ControlArgs`]
/// parser, selected before this parser sees the arguments.
#[derive(Debug, Default, Parser, Clone)]
#[command(
@@ -505,8 +505,8 @@ pub enum WorkerCommand {
},
}
/// CLI-related subcommands. The command-line interface to Warp isn't a full SDK (e.g. with language bindings),
/// but it allows scripting some Warp functionality.
/// CLI-related subcommands. The Galaxy command-line interface isn't a full SDK (e.g. with language bindings),
/// but it allows scripting some Galaxy functionality.
#[derive(Debug, Clone, Subcommand)]
pub enum CliCommand {
/// Interact with Oz.
+11 -42
View File
@@ -1,4 +1,4 @@
//! Implementations for user-facing `warpctrl` command groups.
//! Implementations for user-facing `galaxyctrl` command groups.
use galaxy_core::channel::ChannelState;
use local_control::discovery::InstanceRecord;
use local_control::protocol::{
@@ -55,28 +55,6 @@ pub(super) fn run_surface_command(
SurfaceCommand::Keybindings(command) => {
run_surface_open_command(command, ActionKind::SurfaceKeybindingsOpen, output_format)
}
SurfaceCommand::WarpDrive(command) => match command {
SurfaceOpenToggleCommand::Open(args) => run_action_with_params(
args,
ActionKind::SurfaceWarpDriveOpen,
EmptyParams {},
output_format,
),
SurfaceOpenToggleCommand::Toggle(args) => run_action_with_params(
args,
ActionKind::SurfaceWarpDriveToggle,
EmptyParams {},
output_format,
),
},
SurfaceCommand::ResourceCenter(command) => run_surface_toggle_command(
command,
ActionKind::SurfaceResourceCenterToggle,
output_format,
),
SurfaceCommand::AiAssistant(command) => {
run_surface_toggle_command(command, ActionKind::SurfaceAiAssistantToggle, output_format)
}
SurfaceCommand::CodeReview(command) => match command {
SurfaceOpenToggleCommand::Open(args) => run_action_with_params(
args,
@@ -99,14 +77,6 @@ pub(super) fn run_surface_command(
SurfaceCommand::GlobalSearch(command) => {
run_surface_open_command(command, ActionKind::SurfaceGlobalSearchOpen, output_format)
}
SurfaceCommand::ConversationList(command) => run_surface_open_command(
command,
ActionKind::SurfaceConversationListOpen,
output_format,
),
SurfaceCommand::LeftPanel(command) => {
run_surface_toggle_command(command, ActionKind::SurfaceLeftPanelToggle, output_format)
}
SurfaceCommand::RightPanel(command) => {
run_surface_toggle_command(command, ActionKind::SurfaceRightPanelToggle, output_format)
}
@@ -124,23 +94,18 @@ pub(super) fn run_surface_command(
output_format,
),
},
SurfaceCommand::AgentManagement(command) => run_surface_open_command(
command,
ActionKind::SurfaceAgentManagementOpen,
output_format,
),
}
}
fn render_human_readable(action: ActionKind, data: &serde_json::Value) -> String {
match action {
ActionKind::AppPing => format!(
"Warp instance {} is reachable (protocol version {})",
"Galaxy instance {} is reachable (protocol version {})",
value_or_unknown(data, "instance_id"),
value_or_unknown(data, "protocol_version")
),
ActionKind::AppVersion => format!(
"Warp instance {}\nchannel: {}\napp_id: {}\nprotocol_version: {}",
"Galaxy instance {}\nchannel: {}\napp_id: {}\nprotocol_version: {}",
value_or_unknown(data, "instance_id"),
value_or_unknown(data, "channel"),
value_or_unknown(data, "app_id"),
@@ -191,7 +156,9 @@ pub(super) fn run_instance_command(
) -> Result<(), ControlError> {
match command {
InstanceCommand::List => render_instance_list(
local_control::discovery::list_instances(&ChannelState::channel().to_string()),
local_control::discovery::list_instances(
ChannelState::channel().local_control_channel_name(),
),
output_format,
),
InstanceCommand::Inspect(args) => run_action_with_params(
@@ -203,7 +170,7 @@ pub(super) fn run_instance_command(
}
}
/// JSON payload for `warpctrl instance list`.
/// JSON payload for `galaxyctrl instance list`.
#[derive(Serialize)]
pub(super) struct InstanceListOutput {
instances: Vec<InstanceSummary>,
@@ -249,7 +216,7 @@ fn render_instance_list(
OutputFormat::Ndjson => write_json_line(&output),
OutputFormat::Pretty | OutputFormat::Text => {
if output.instances.is_empty() {
println!("No running Warp instances with local control were found.");
println!("No running Galaxy instances with local control were found.");
return Ok(());
}
for instance in &output.instances {
@@ -775,7 +742,9 @@ fn run_action_with_params<T: Serialize>(
output_format: OutputFormat,
) -> Result<(), ControlError> {
let selector = instance_selector(&args);
let records = local_control::discovery::list_instances(&ChannelState::channel().to_string());
let records = local_control::discovery::list_instances(
ChannelState::channel().local_control_channel_name(),
);
let target = target_selector(&args)?;
let instance = select_instance(&records, &selector)?;
let mut request = RequestEnvelope::new(Action::with_params(action, params)?);
@@ -1,8 +1,8 @@
//! Shell completion generation for `warpctrl`.
//! Shell completion generation for `galaxyctrl`.
use clap_complete::aot::{Shell, generate};
use local_control::protocol::{ControlError, ErrorCode};
use crate::local_control::ControlArgs;
use crate::local_control::{ControlArgs, normalized_control_command_name};
pub(super) fn generate_completions_to_stdout(shell: Option<Shell>) -> Result<(), ControlError> {
let shell = shell.or_else(Shell::from_env).ok_or_else(|| {
@@ -12,7 +12,8 @@ pub(super) fn generate_completions_to_stdout(shell: Option<Shell>) -> Result<(),
)
})?;
let mut cmd = ControlArgs::clap_command();
let bin_name = crate::binary_name().unwrap_or_else(|| "warpctrl".to_owned());
let invocation_name = crate::binary_name();
let bin_name = normalized_control_command_name(invocation_name.as_deref());
generate(shell, &mut cmd, bin_name, &mut std::io::stdout());
Ok(())
}
@@ -21,7 +22,7 @@ pub(super) fn generate_completions_to_stdout(shell: Option<Shell>) -> Result<(),
pub(crate) fn generate_completion_string(shell: Shell) -> Result<String, ControlError> {
let mut cmd = ControlArgs::clap_command();
let mut output = Vec::new();
generate(shell, &mut cmd, "warpctrl", &mut output);
generate(shell, &mut cmd, "galaxyctrl", &mut output);
String::from_utf8(output).map_err(|err| {
ControlError::with_details(
ErrorCode::Internal,
+66 -84
View File
@@ -1,4 +1,4 @@
//! Command-line interface for controlling a running local Warp app.
//! Command-line interface for controlling a running local Galaxy app.
mod commands;
mod completions;
mod output;
@@ -19,15 +19,22 @@ use output::write_control_error;
use crate::agent::OutputFormat;
/// Hidden flag used by the channel-specific Warp app binary to enter `warpctrl` mode.
pub const CONTROL_MODE_FLAG: &str = "--warpctrl";
/// Hidden flag used by the channel-specific Galaxy app binary to enter `galaxyctrl` mode.
pub const CONTROL_MODE_FLAG: &str = "--galaxyctrl";
/// Parsed top-level arguments for `warpctrl`.
fn normalized_control_command_name(invocation_name: Option<&str>) -> String {
invocation_name
.filter(|name| *name == "galaxyctrl" || name.starts_with("galaxyctrl-"))
.unwrap_or("galaxyctrl")
.to_owned()
}
/// Parsed top-level arguments for `galaxyctrl`.
#[derive(Debug, Parser)]
#[command(
name = "warpctrl",
display_name = "warpctrl",
about = "Control a running local Warp app instance"
name = "galaxyctrl",
display_name = "galaxyctrl",
about = "Control a running local Galaxy app instance"
)]
pub struct ControlArgs {
/// Set the output format.
@@ -36,7 +43,7 @@ pub struct ControlArgs {
global = true,
value_enum,
default_value_t = OutputFormat::Pretty,
env = "WARP_OUTPUT_FORMAT"
env = "GALAXY_OUTPUT_FORMAT"
)]
pub output_format: OutputFormat,
@@ -59,15 +66,15 @@ pub enum ActionCatalogCommand {
impl ControlArgs {
pub fn from_env() -> Self {
let bin_name = crate::binary_name().unwrap_or_else(|| "warpctrl".to_owned());
let bin_name = crate::binary_name().unwrap_or_else(|| "galaxyctrl".to_owned());
Self::try_parse_from_args(std::env::args_os(), bin_name).unwrap_or_else(|err| err.exit())
}
/// Parse Warp Control arguments only when the wrapper-injected mode flag is present.
/// Parse Galaxy Control arguments only when the wrapper-injected mode flag is present.
///
/// Startup calls this before the normal Warp/Oz parser. Arguments through
/// `--warpctrl` are removed, and the remaining arguments are parsed as if
/// the standalone command name were `warpctrl`.
/// Startup calls this before the normal Galaxy parser. Arguments through
/// `--galaxyctrl` are removed, and the remaining arguments are parsed as if
/// the standalone command name were `galaxyctrl`.
pub fn from_control_mode_env() -> Option<Self> {
Self::try_parse_control_mode_from(std::env::args_os())
.map(|result| result.unwrap_or_else(|err| err.exit()))
@@ -79,7 +86,7 @@ impl ControlArgs {
I: IntoIterator<Item = T>,
T: Into<OsString>,
{
let mut stripped_args = vec![OsString::from("warpctrl")];
let mut stripped_args = vec![OsString::from("galaxyctrl")];
let mut found_control_mode = false;
for arg in args {
@@ -93,11 +100,12 @@ impl ControlArgs {
stripped_args.push(arg);
}
found_control_mode.then(|| Self::try_parse_from_args(stripped_args, "warpctrl"))
found_control_mode.then(|| Self::try_parse_from_args(stripped_args, "galaxyctrl"))
}
pub fn clap_command() -> clap::Command {
let bin_name = crate::binary_name().unwrap_or_else(|| "warpctrl".to_owned());
let invocation_name = crate::binary_name();
let bin_name = normalized_control_command_name(invocation_name.as_deref());
Self::clap_command_for_bin_name(bin_name)
}
@@ -133,13 +141,13 @@ impl ControlArgs {
}
}
/// Top-level `warpctrl` command groups.
/// Top-level `galaxyctrl` command groups.
#[derive(Debug, Clone, Subcommand)]
pub enum ControlCommand {
/// Inspect local Warp app instances.
/// Inspect local Galaxy app instances.
#[command(subcommand)]
Instance(InstanceCommand),
/// Inspect a selected local Warp app.
/// Inspect and control a selected local Galaxy app.
#[command(subcommand)]
App(AppCommand),
/// Inspect local-control capabilities.
@@ -149,34 +157,34 @@ pub enum ControlCommand {
#[command(subcommand)]
Action(ActionCatalogCommand),
/// Inspect local Warp windows.
/// Control local Galaxy windows.
#[command(subcommand)]
Window(WindowCommand),
/// Control local Warp tabs.
/// Control local Galaxy tabs.
#[command(subcommand)]
Tab(TabCommand),
/// Inspect local Warp panes.
/// Control local Galaxy panes.
#[command(subcommand)]
Pane(PaneCommand),
/// Inspect local Warp sessions.
/// Control local Galaxy sessions.
#[command(subcommand)]
Session(SessionCommand),
/// Inspect terminal input state.
/// Edit terminal input without submitting it.
#[command(subcommand)]
Input(InputCommand),
/// Inspect Warp themes.
/// Inspect and change Galaxy themes.
#[command(subcommand)]
Theme(ThemeCommand),
/// Inspect appearance state.
/// Inspect and change Galaxy appearance.
#[command(subcommand)]
Appearance(AppearanceCommand),
/// Inspect allowlisted settings.
/// Inspect and change allowlisted settings.
#[command(subcommand)]
Setting(SettingCommand),
@@ -184,29 +192,29 @@ pub enum ControlCommand {
#[command(subcommand)]
Keybinding(KeybindingCommand),
/// Inspect open file app-state metadata.
/// Open files in Galaxy.
#[command(subcommand)]
File(FileCommand),
/// Open or toggle local Warp surfaces.
/// Open or toggle local Galaxy surfaces.
#[command(subcommand)]
Surface(SurfaceCommand),
/// Generate shell completions for your shell to stdout.
///
/// For bash, add the following to ~/.bashrc:
/// source <(path/to/warpctrl completions bash)
/// source <(path/to/galaxyctrl completions bash)
///
/// For zsh, add the following to ~/.zshrc:
/// source <(path/to/warpctrl completions zsh)
/// source <(path/to/galaxyctrl completions zsh)
///
/// For fish, add the following to ~/.config/fish/config.fish:
/// path/to/warpctrl completions fish | source
/// path/to/galaxyctrl completions fish | source
///
/// For Powershell, add the following to $PROFILE:
/// path\to\warpctrl completions powershell | Out-String | Invoke-Expression
/// path\to\galaxyctrl completions powershell | Out-String | Invoke-Expression
///
/// If no shell is provided, this defaults to the shell that Warp was run from.
/// If no shell is provided, this defaults to the shell that Galaxy was run from.
#[command(verbatim_doc_comment)]
Completions {
/// Shell to generate completions for.
@@ -215,29 +223,29 @@ pub enum ControlCommand {
},
}
/// Commands that inspect locally discoverable Warp instances.
/// Commands that inspect locally discoverable Galaxy instances.
#[derive(Debug, Clone, Subcommand)]
pub enum InstanceCommand {
/// List locally discoverable Warp instances.
/// List locally discoverable Galaxy instances.
List,
/// Print app, protocol, active target, and action metadata for the selected instance.
Inspect(TargetArgs),
}
/// Commands that inspect the selected Warp app instance.
/// Commands that inspect and control the selected Galaxy app instance.
#[derive(Debug, Clone, Subcommand)]
pub enum AppCommand {
/// Check that the selected local Warp app responds.
/// Check that the selected local Galaxy app responds.
Ping(TargetArgs),
/// Print protocol and build identity metadata for the selected local Warp app.
/// Print protocol and build identity metadata for the selected local Galaxy app.
Version(TargetArgs),
/// Print the active window/tab/pane/session chain.
Active(TargetArgs),
/// Focus the selected local Warp app.
/// Focus the selected local Galaxy app.
Focus(TargetArgs),
}
@@ -256,10 +264,10 @@ pub enum CapabilityCommand {
#[derive(Debug, Clone, Subcommand)]
pub enum WindowCommand {
/// List windows in the selected local Warp app.
/// List windows in the selected local Galaxy app.
List(TargetArgs),
/// Inspect one window in the selected local Warp app.
/// Inspect one window in the selected local Galaxy app.
Inspect(TargetArgs),
/// Create a new window.
@@ -272,13 +280,13 @@ pub enum WindowCommand {
Close(TargetArgs),
}
/// Commands that control tabs in the selected Warp app instance.
/// Commands that control tabs in the selected Galaxy app instance.
#[derive(Debug, Clone, Subcommand)]
pub enum TabCommand {
/// List tabs in the selected local Warp app.
/// List tabs in the selected local Galaxy app.
List(TargetArgs),
/// Inspect one tab in the selected local Warp app.
/// Inspect one tab in the selected local Galaxy app.
Inspect(TargetArgs),
/// Create a new terminal tab in the active window.
@@ -314,13 +322,13 @@ pub enum TabColorCommand {
Clear(TargetArgs),
}
/// Commands that inspect local Warp panes.
/// Commands that control local Galaxy panes.
#[derive(Debug, Clone, Subcommand)]
pub enum PaneCommand {
/// List panes in the selected local Warp app.
/// List panes in the selected local Galaxy app.
List(TargetArgs),
/// Inspect one pane in the selected local Warp app.
/// Inspect one pane in the selected local Galaxy app.
Inspect(TargetArgs),
/// Split the active pane.
@@ -351,13 +359,13 @@ pub enum PaneCommand {
ResetName(TargetArgs),
}
/// Commands that inspect local Warp sessions.
/// Commands that control local Galaxy sessions.
#[derive(Debug, Clone, Subcommand)]
pub enum SessionCommand {
/// List sessions in the selected local Warp app.
/// List sessions in the selected local Galaxy app.
List(TargetArgs),
/// Inspect one session in the selected local Warp app.
/// Inspect one session in the selected local Galaxy app.
Inspect(TargetArgs),
/// Activate a session.
@@ -384,7 +392,7 @@ pub enum InputCommand {
#[derive(Debug, Clone, Subcommand)]
pub enum SurfaceCommand {
/// List available and unavailable tour surfaces.
/// List available and unavailable Galaxy surfaces.
List(TargetArgs),
/// Open settings surfaces.
#[command(subcommand)]
@@ -405,18 +413,6 @@ pub enum SurfaceCommand {
#[command(subcommand)]
Keybindings(SurfaceOpenCommand),
/// Open or toggle Warp Drive.
#[command(subcommand)]
WarpDrive(SurfaceOpenToggleCommand),
/// Toggle the resource center.
#[command(subcommand)]
ResourceCenter(SurfaceToggleCommand),
/// Toggle the AI assistant.
#[command(subcommand)]
AiAssistant(SurfaceToggleCommand),
/// Open or toggle code review.
#[command(subcommand)]
CodeReview(SurfaceOpenToggleCommand),
@@ -429,14 +425,6 @@ pub enum SurfaceCommand {
#[command(subcommand)]
GlobalSearch(SurfaceOpenCommand),
/// Open the conversation list.
#[command(subcommand)]
ConversationList(SurfaceOpenCommand),
/// Toggle the left panel.
#[command(subcommand)]
LeftPanel(SurfaceToggleCommand),
/// Toggle the right panel.
#[command(subcommand)]
RightPanel(SurfaceToggleCommand),
@@ -444,10 +432,6 @@ pub enum SurfaceCommand {
/// Open or toggle vertical tabs.
#[command(subcommand)]
VerticalTabs(SurfaceOpenToggleCommand),
/// Open agent management.
#[command(subcommand)]
AgentManagement(SurfaceOpenCommand),
}
#[derive(Debug, Clone, Subcommand)]
@@ -482,7 +466,7 @@ pub enum SurfaceToggleCommand {
Toggle(TargetArgs),
}
/// Commands that inspect Warp themes.
/// Commands that inspect and change Galaxy themes.
#[derive(Debug, Clone, Subcommand)]
pub enum ThemeCommand {
/// List available themes.
@@ -494,7 +478,7 @@ pub enum ThemeCommand {
/// Set the current theme.
Set(ThemeSetArgs),
/// Set whether Warp follows the system theme.
/// Set whether Galaxy follows the system theme.
SystemSet(ThemeSystemSetArgs),
/// Set the light theme used when following the system theme.
@@ -554,18 +538,18 @@ pub enum KeybindingCommand {
#[derive(Debug, Clone, Subcommand)]
pub enum FileCommand {
/// Open a file in Warp.
/// Open a file in Galaxy.
Open(FileOpenArgs),
}
/// Exact selectors for a target within the selected Warp instance.
/// Exact selectors for a target within the selected Galaxy instance.
#[derive(Debug, Clone, Args, Default)]
pub struct TargetArgs {
/// Target a specific local Warp instance id from `warpctrl instance list`.
/// Target a specific local Galaxy instance id from `galaxyctrl instance list`.
#[arg(long = "instance", conflicts_with = "pid")]
pub instance: Option<String>,
/// Target a specific local Warp process id.
/// Target a specific local Galaxy process id.
#[arg(long = "pid", conflicts_with = "instance")]
pub pid: Option<u32>,
@@ -815,7 +799,6 @@ pub struct KeybindingGetArgs {
pub enum CliTabType {
Terminal,
Agent,
CloudAgent,
Default,
}
@@ -824,7 +807,6 @@ impl From<CliTabType> for local_control::protocol::TabType {
match value {
CliTabType::Terminal => Self::Terminal,
CliTabType::Agent => Self::Agent,
CliTabType::CloudAgent => Self::CloudAgent,
CliTabType::Default => Self::Default,
}
}
@@ -1,4 +1,4 @@
//! Output rendering helpers for `warpctrl`.
//! Output rendering helpers for `galaxyctrl`.
use std::io::Write as _;
use local_control::protocol::{ControlError, ErrorCode};
@@ -6,7 +6,7 @@ use serde::Serialize;
use crate::agent::OutputFormat;
/// JSON/NDJSON error payload emitted by `warpctrl`.
/// JSON/NDJSON error payload emitted by `galaxyctrl`.
#[derive(Serialize)]
pub(crate) struct ErrorSummary<'a> {
pub ok: bool,
+171 -160
View File
@@ -9,7 +9,7 @@ use super::*;
#[test]
fn parses_typed_create_and_setting_list_params() {
let args = ControlArgs::try_parse_from([
"warpctrl",
"galaxyctrl",
"tab",
"create",
"--type",
@@ -28,7 +28,7 @@ fn parses_typed_create_and_setting_list_params() {
assert_eq!(args.target.session.as_deref(), Some("session_1"));
let args =
ControlArgs::try_parse_from(["warpctrl", "setting", "list", "--namespace", "editor"])
ControlArgs::try_parse_from(["galaxyctrl", "setting", "list", "--namespace", "editor"])
.expect("setting list parses");
let ControlCommand::Setting(SettingCommand::List(args)) = args.command else {
panic!("expected setting list command");
@@ -39,7 +39,7 @@ fn parses_typed_create_and_setting_list_params() {
#[test]
fn rejects_conflicting_instance_selectors() {
let err = ControlArgs::try_parse_from([
"warpctrl",
"galaxyctrl",
"tab",
"create",
"--instance",
@@ -53,14 +53,15 @@ fn rejects_conflicting_instance_selectors() {
#[test]
fn parses_instance_and_pid_selectors() {
let args = ControlArgs::try_parse_from(["warpctrl", "tab", "create", "--instance", "inst_123"])
.expect("instance selector parses");
let args =
ControlArgs::try_parse_from(["galaxyctrl", "tab", "create", "--instance", "inst_123"])
.expect("instance selector parses");
let ControlCommand::Tab(TabCommand::Create(create)) = args.command else {
panic!("expected tab create command");
};
assert_eq!(create.target.instance.as_deref(), Some("inst_123"));
let args = ControlArgs::try_parse_from(["warpctrl", "app", "ping", "--pid", "123"])
let args = ControlArgs::try_parse_from(["galaxyctrl", "app", "ping", "--pid", "123"])
.expect("pid selector parses");
let ControlCommand::App(AppCommand::Ping(target)) = args.command else {
panic!("expected app ping command");
@@ -71,7 +72,7 @@ fn parses_instance_and_pid_selectors() {
#[test]
fn surface_list_accepts_instance_selection() {
let args =
ControlArgs::try_parse_from(["warpctrl", "surface", "list", "--instance", "inst_123"])
ControlArgs::try_parse_from(["galaxyctrl", "surface", "list", "--instance", "inst_123"])
.expect("surface list instance selector parses");
let ControlCommand::Surface(SurfaceCommand::List(target)) = args.command else {
panic!("expected surface list command");
@@ -82,17 +83,25 @@ fn surface_list_accepts_instance_selection() {
#[test]
fn rejects_excluded_command_routes() {
for args in [
vec!["warpctrl", "history", "list"],
vec!["warpctrl", "block", "list"],
vec!["warpctrl", "block", "inspect", "block_1"],
vec!["warpctrl", "block", "output", "block_1"],
vec!["warpctrl", "input", "get"],
vec!["warpctrl", "input", "clear"],
vec!["warpctrl", "input", "mode", "set", "agent"],
vec!["warpctrl", "input", "run", "pwd"],
vec!["warpctrl", "file", "list"],
vec!["warpctrl", "drive", "list"],
vec!["warpctrl", "auth", "status"],
vec!["galaxyctrl", "history", "list"],
vec!["galaxyctrl", "block", "list"],
vec!["galaxyctrl", "block", "inspect", "block_1"],
vec!["galaxyctrl", "block", "output", "block_1"],
vec!["galaxyctrl", "input", "get"],
vec!["galaxyctrl", "input", "clear"],
vec!["galaxyctrl", "input", "mode", "set", "agent"],
vec!["galaxyctrl", "input", "run", "pwd"],
vec!["galaxyctrl", "file", "list"],
vec!["galaxyctrl", "drive", "list"],
vec!["galaxyctrl", "auth", "status"],
vec!["galaxyctrl", "surface", "warp-drive", "open"],
vec!["galaxyctrl", "surface", "warp-drive", "toggle"],
vec!["galaxyctrl", "surface", "resource-center", "toggle"],
vec!["galaxyctrl", "surface", "ai-assistant", "toggle"],
vec!["galaxyctrl", "surface", "conversation-list", "open"],
vec!["galaxyctrl", "surface", "agent-management", "open"],
vec!["galaxyctrl", "surface", "left-panel", "toggle"],
vec!["galaxyctrl", "tab", "create", "--type", "cloud-agent"],
] {
assert!(ControlArgs::try_parse_from(args).is_err());
}
@@ -100,7 +109,7 @@ fn rejects_excluded_command_routes() {
#[test]
fn parses_first_slice_instance_list() {
let args = ControlArgs::try_parse_from(["warpctrl", "instance", "list"])
let args = ControlArgs::try_parse_from(["galaxyctrl", "instance", "list"])
.expect("instance list parses");
assert!(matches!(
args.command,
@@ -110,31 +119,31 @@ fn parses_first_slice_instance_list() {
#[test]
fn parses_first_slice_app_smoke_metadata_commands() {
assert!(ControlArgs::try_parse_from(["warpctrl", "app", "ping"]).is_ok());
assert!(ControlArgs::try_parse_from(["warpctrl", "app", "version"]).is_ok());
assert!(ControlArgs::try_parse_from(["warpctrl", "app", "active"]).is_ok());
assert!(ControlArgs::try_parse_from(["warpctrl", "app", "focus"]).is_ok());
assert!(ControlArgs::try_parse_from(["galaxyctrl", "app", "ping"]).is_ok());
assert!(ControlArgs::try_parse_from(["galaxyctrl", "app", "version"]).is_ok());
assert!(ControlArgs::try_parse_from(["galaxyctrl", "app", "active"]).is_ok());
assert!(ControlArgs::try_parse_from(["galaxyctrl", "app", "focus"]).is_ok());
}
#[test]
fn parses_catalog_metadata_commands() {
let args =
ControlArgs::try_parse_from(["warpctrl", "action", "inspect", "surface.settings.open"])
ControlArgs::try_parse_from(["galaxyctrl", "action", "inspect", "surface.settings.open"])
.expect("action inspect parses");
let ControlCommand::Action(ActionCatalogCommand::Inspect { action }) = args.command else {
panic!("expected action inspect command");
};
assert_eq!(action, "surface.settings.open");
assert!(ControlArgs::try_parse_from(["warpctrl", "action", "list"]).is_ok());
assert!(ControlArgs::try_parse_from(["warpctrl", "capability", "list"]).is_ok());
assert!(ControlArgs::try_parse_from(["galaxyctrl", "action", "list"]).is_ok());
assert!(ControlArgs::try_parse_from(["galaxyctrl", "capability", "list"]).is_ok());
assert!(
ControlArgs::try_parse_from(["warpctrl", "capability", "inspect", "tab.create"]).is_ok()
ControlArgs::try_parse_from(["galaxyctrl", "capability", "inspect", "tab.create"]).is_ok()
);
}
#[test]
fn parses_control_mode_args_after_hidden_flag() {
let args = ControlArgs::try_parse_control_mode_from(["warp", "--warpctrl", "tab", "create"])
let args = ControlArgs::try_parse_control_mode_from(["warp", "--galaxyctrl", "tab", "create"])
.expect("control mode flag is present")
.expect("control mode args parse");
assert!(matches!(
@@ -150,7 +159,7 @@ fn ignores_args_without_control_mode_flag() {
#[test]
fn parses_completion_generation_command() {
let args = ControlArgs::try_parse_from(["warpctrl", "completions", "bash"])
let args = ControlArgs::try_parse_from(["galaxyctrl", "completions", "bash"])
.expect("completions parses");
assert!(matches!(
args.command,
@@ -163,7 +172,7 @@ fn parses_completion_generation_command() {
#[test]
fn parses_exact_window_tab_pane_and_session_selectors() {
let args = ControlArgs::try_parse_from([
"warpctrl",
"galaxyctrl",
"session",
"inspect",
"--window-title",
@@ -194,7 +203,7 @@ fn instance_list_output_serializes_empty_and_populated_lists() {
let record = local_control::discovery::InstanceRecord::for_current_process(
None,
"dev",
"dev.warp.Warp",
"dev.galaxy.Galaxy",
Some("v0.1.0".to_owned()),
Vec::new(),
);
@@ -203,7 +212,10 @@ fn instance_list_output_serializes_empty_and_populated_lists() {
.expect("populated list serializes");
assert_eq!(populated["instances"][0]["instance_id"], json!(instance_id));
assert_eq!(populated["instances"][0]["channel"], json!("dev"));
assert_eq!(populated["instances"][0]["app_id"], json!("dev.warp.Warp"));
assert_eq!(
populated["instances"][0]["app_id"],
json!("dev.galaxy.Galaxy")
);
assert_eq!(populated["instances"][0]["app_version"], json!("v0.1.0"));
}
@@ -258,17 +270,39 @@ fn generated_bash_completions_include_mutating_command_groups() {
generate_completion_string(Shell::Bash).expect("bash completions render to UTF-8");
assert!(completions.contains("surface"));
assert!(completions.contains("command-palette"));
assert!(completions.contains("warp-drive"));
assert!(completions.contains("resource-center"));
assert!(!completions.contains("warp-drive"));
assert!(!completions.contains("resource-center"));
assert!(!completions.contains("ai-assistant"));
assert!(!completions.contains("conversation-list"));
assert!(!completions.contains("agent-management"));
assert!(!completions.contains("left-panel"));
assert!(!completions.contains("cloud-agent"));
assert!(completions.contains("activate"));
assert!(completions.contains("split"));
assert!(!completions.contains("history"));
assert!(!completions.contains("share-to-team"));
}
#[test]
fn completion_name_never_falls_back_to_the_forwarded_app_binary() {
assert_eq!(
normalized_control_command_name(Some("galaxyctrl-dev")),
"galaxyctrl-dev"
);
assert_eq!(
normalized_control_command_name(Some("galaxy-dev")),
"galaxyctrl"
);
assert_eq!(
normalized_control_command_name(Some("galaxy-oss")),
"galaxyctrl"
);
assert_eq!(normalized_control_command_name(None), "galaxyctrl");
}
#[test]
fn structured_error_output_uses_stable_error_code() {
let error = ControlError::new(ErrorCode::NoInstance, "no local Warp control instances");
let error = ControlError::new(ErrorCode::NoInstance, "no local Galaxy control instances");
let value = serde_json::to_value(ErrorSummary {
ok: false,
error: &error,
@@ -278,7 +312,7 @@ fn structured_error_output_uses_stable_error_code() {
assert_eq!(value["error"]["code"], json!("no_instance"));
assert_eq!(
value["error"]["message"],
json!("no local Warp control instances")
json!("no local Galaxy control instances")
);
}
@@ -307,75 +341,87 @@ fn retained_action_examples() -> Vec<(ActionKind, Vec<&'static str>)> {
vec![
(
ActionKind::InstanceList,
vec!["warpctrl", "instance", "list"],
vec!["galaxyctrl", "instance", "list"],
),
(
ActionKind::InstanceInspect,
vec!["warpctrl", "instance", "inspect"],
vec!["galaxyctrl", "instance", "inspect"],
),
(ActionKind::AppPing, vec!["warpctrl", "app", "ping"]),
(ActionKind::AppVersion, vec!["warpctrl", "app", "version"]),
(ActionKind::AppActive, vec!["warpctrl", "app", "active"]),
(ActionKind::AppFocus, vec!["warpctrl", "app", "focus"]),
(ActionKind::AppPing, vec!["galaxyctrl", "app", "ping"]),
(ActionKind::AppVersion, vec!["galaxyctrl", "app", "version"]),
(ActionKind::AppActive, vec!["galaxyctrl", "app", "active"]),
(ActionKind::AppFocus, vec!["galaxyctrl", "app", "focus"]),
(
ActionKind::CapabilityList,
vec!["warpctrl", "capability", "list"],
vec!["galaxyctrl", "capability", "list"],
),
(
ActionKind::CapabilityInspect,
vec!["warpctrl", "capability", "inspect", "tab.create"],
vec!["galaxyctrl", "capability", "inspect", "tab.create"],
),
(ActionKind::WindowList, vec!["warpctrl", "window", "list"]),
(ActionKind::WindowList, vec!["galaxyctrl", "window", "list"]),
(
ActionKind::WindowInspect,
vec!["warpctrl", "window", "inspect"],
vec!["galaxyctrl", "window", "inspect"],
),
(
ActionKind::WindowCreate,
vec!["warpctrl", "window", "create"],
vec!["galaxyctrl", "window", "create"],
),
(
ActionKind::WindowFocus,
vec!["galaxyctrl", "window", "focus"],
),
(
ActionKind::WindowClose,
vec!["galaxyctrl", "window", "close"],
),
(ActionKind::TabList, vec!["galaxyctrl", "tab", "list"]),
(ActionKind::TabInspect, vec!["galaxyctrl", "tab", "inspect"]),
(ActionKind::TabCreate, vec!["galaxyctrl", "tab", "create"]),
(
ActionKind::TabActivate,
vec!["galaxyctrl", "tab", "activate"],
),
(ActionKind::WindowFocus, vec!["warpctrl", "window", "focus"]),
(ActionKind::WindowClose, vec!["warpctrl", "window", "close"]),
(ActionKind::TabList, vec!["warpctrl", "tab", "list"]),
(ActionKind::TabInspect, vec!["warpctrl", "tab", "inspect"]),
(ActionKind::TabCreate, vec!["warpctrl", "tab", "create"]),
(ActionKind::TabActivate, vec!["warpctrl", "tab", "activate"]),
(
ActionKind::TabMove,
vec!["warpctrl", "tab", "move", "--direction", "next"],
vec!["galaxyctrl", "tab", "move", "--direction", "next"],
),
(ActionKind::TabClose, vec!["warpctrl", "tab", "close"]),
(ActionKind::TabClose, vec!["galaxyctrl", "tab", "close"]),
(
ActionKind::TabRename,
vec!["warpctrl", "tab", "rename", "docs"],
vec!["galaxyctrl", "tab", "rename", "docs"],
),
(
ActionKind::TabResetName,
vec!["warpctrl", "tab", "reset-name"],
vec!["galaxyctrl", "tab", "reset-name"],
),
(
ActionKind::TabColorSet,
vec!["warpctrl", "tab", "color", "set", "red"],
vec!["galaxyctrl", "tab", "color", "set", "red"],
),
(
ActionKind::TabColorClear,
vec!["warpctrl", "tab", "color", "clear"],
vec!["galaxyctrl", "tab", "color", "clear"],
),
(ActionKind::PaneList, vec!["galaxyctrl", "pane", "list"]),
(
ActionKind::PaneInspect,
vec!["galaxyctrl", "pane", "inspect"],
),
(ActionKind::PaneList, vec!["warpctrl", "pane", "list"]),
(ActionKind::PaneInspect, vec!["warpctrl", "pane", "inspect"]),
(
ActionKind::PaneSplit,
vec!["warpctrl", "pane", "split", "--direction", "right"],
vec!["galaxyctrl", "pane", "split", "--direction", "right"],
),
(ActionKind::PaneFocus, vec!["warpctrl", "pane", "focus"]),
(ActionKind::PaneFocus, vec!["galaxyctrl", "pane", "focus"]),
(
ActionKind::PaneNavigate,
vec!["warpctrl", "pane", "navigate", "--direction", "next"],
vec!["galaxyctrl", "pane", "navigate", "--direction", "next"],
),
(
ActionKind::PaneResize,
vec![
"warpctrl",
"galaxyctrl",
"pane",
"resize",
"--direction",
@@ -386,199 +432,183 @@ fn retained_action_examples() -> Vec<(ActionKind, Vec<&'static str>)> {
),
(
ActionKind::PaneMaximize,
vec!["warpctrl", "pane", "maximize"],
vec!["galaxyctrl", "pane", "maximize"],
),
(
ActionKind::PaneUnmaximize,
vec!["warpctrl", "pane", "unmaximize"],
vec!["galaxyctrl", "pane", "unmaximize"],
),
(ActionKind::PaneClose, vec!["warpctrl", "pane", "close"]),
(ActionKind::PaneClose, vec!["galaxyctrl", "pane", "close"]),
(
ActionKind::PaneRename,
vec!["warpctrl", "pane", "rename", "server"],
vec!["galaxyctrl", "pane", "rename", "server"],
),
(
ActionKind::PaneResetName,
vec!["warpctrl", "pane", "reset-name"],
vec!["galaxyctrl", "pane", "reset-name"],
),
(
ActionKind::SessionList,
vec!["galaxyctrl", "session", "list"],
),
(ActionKind::SessionList, vec!["warpctrl", "session", "list"]),
(
ActionKind::SessionInspect,
vec!["warpctrl", "session", "inspect"],
vec!["galaxyctrl", "session", "inspect"],
),
(
ActionKind::SessionActivate,
vec!["warpctrl", "session", "activate"],
vec!["galaxyctrl", "session", "activate"],
),
(
ActionKind::SessionPrevious,
vec!["warpctrl", "session", "previous"],
vec!["galaxyctrl", "session", "previous"],
),
(
ActionKind::SessionNext,
vec!["galaxyctrl", "session", "next"],
),
(ActionKind::SessionNext, vec!["warpctrl", "session", "next"]),
(
ActionKind::SessionReopenClosed,
vec!["warpctrl", "session", "reopen-closed"],
vec!["galaxyctrl", "session", "reopen-closed"],
),
(
ActionKind::InputInsert,
vec!["warpctrl", "input", "insert", "hello"],
vec!["galaxyctrl", "input", "insert", "hello"],
),
(
ActionKind::InputReplace,
vec!["warpctrl", "input", "replace", "hello"],
vec!["galaxyctrl", "input", "replace", "hello"],
),
(ActionKind::ThemeList, vec!["warpctrl", "theme", "list"]),
(ActionKind::ThemeGet, vec!["warpctrl", "theme", "get"]),
(ActionKind::ThemeList, vec!["galaxyctrl", "theme", "list"]),
(ActionKind::ThemeGet, vec!["galaxyctrl", "theme", "get"]),
(
ActionKind::ThemeSet,
vec!["warpctrl", "theme", "set", "Dracula"],
vec!["galaxyctrl", "theme", "set", "Dracula"],
),
(
ActionKind::ThemeSystemSet,
vec!["warpctrl", "theme", "system-set", "true"],
vec!["galaxyctrl", "theme", "system-set", "true"],
),
(
ActionKind::ThemeLightSet,
vec!["warpctrl", "theme", "light-set", "Light"],
vec!["galaxyctrl", "theme", "light-set", "Light"],
),
(
ActionKind::ThemeDarkSet,
vec!["warpctrl", "theme", "dark-set", "Dark"],
vec!["galaxyctrl", "theme", "dark-set", "Dark"],
),
(
ActionKind::AppearanceGet,
vec!["warpctrl", "appearance", "get"],
vec!["galaxyctrl", "appearance", "get"],
),
(
ActionKind::AppearanceFontSizeIncrease,
vec!["warpctrl", "appearance", "font-size-increase"],
vec!["galaxyctrl", "appearance", "font-size-increase"],
),
(
ActionKind::AppearanceFontSizeDecrease,
vec!["warpctrl", "appearance", "font-size-decrease"],
vec!["galaxyctrl", "appearance", "font-size-decrease"],
),
(
ActionKind::AppearanceFontSizeReset,
vec!["warpctrl", "appearance", "font-size-reset"],
vec!["galaxyctrl", "appearance", "font-size-reset"],
),
(
ActionKind::AppearanceZoomIncrease,
vec!["warpctrl", "appearance", "zoom-increase"],
vec!["galaxyctrl", "appearance", "zoom-increase"],
),
(
ActionKind::AppearanceZoomDecrease,
vec!["warpctrl", "appearance", "zoom-decrease"],
vec!["galaxyctrl", "appearance", "zoom-decrease"],
),
(
ActionKind::AppearanceZoomReset,
vec!["warpctrl", "appearance", "zoom-reset"],
vec!["galaxyctrl", "appearance", "zoom-reset"],
),
(
ActionKind::SettingList,
vec!["galaxyctrl", "setting", "list"],
),
(ActionKind::SettingList, vec!["warpctrl", "setting", "list"]),
(
ActionKind::SettingGet,
vec!["warpctrl", "setting", "get", "font_size"],
vec!["galaxyctrl", "setting", "get", "font_size"],
),
(
ActionKind::SettingSet,
vec!["warpctrl", "setting", "set", "font_size", "14"],
vec!["galaxyctrl", "setting", "set", "font_size", "14"],
),
(
ActionKind::SettingToggle,
vec!["warpctrl", "setting", "toggle", "autosuggestions"],
vec!["galaxyctrl", "setting", "toggle", "autosuggestions"],
),
(
ActionKind::KeybindingList,
vec!["warpctrl", "keybinding", "list"],
vec!["galaxyctrl", "keybinding", "list"],
),
(
ActionKind::KeybindingGet,
vec!["warpctrl", "keybinding", "get", "copy"],
vec!["galaxyctrl", "keybinding", "get", "copy"],
),
(ActionKind::ActionList, vec!["warpctrl", "action", "list"]),
(ActionKind::ActionList, vec!["galaxyctrl", "action", "list"]),
(
ActionKind::ActionInspect,
vec!["warpctrl", "action", "inspect", "tab.create"],
vec!["galaxyctrl", "action", "inspect", "tab.create"],
),
(
ActionKind::SurfaceList,
vec!["galaxyctrl", "surface", "list"],
),
(ActionKind::SurfaceList, vec!["warpctrl", "surface", "list"]),
(
ActionKind::SurfaceSettingsOpen,
vec!["warpctrl", "surface", "settings", "open"],
vec!["galaxyctrl", "surface", "settings", "open"],
),
(
ActionKind::SurfaceCommandPaletteOpen,
vec!["warpctrl", "surface", "command-palette", "open"],
vec!["galaxyctrl", "surface", "command-palette", "open"],
),
(
ActionKind::SurfaceCommandSearchOpen,
vec!["warpctrl", "surface", "command-search", "open"],
vec!["galaxyctrl", "surface", "command-search", "open"],
),
(
ActionKind::SurfaceThemePickerOpen,
vec!["warpctrl", "surface", "theme-picker", "open"],
vec!["galaxyctrl", "surface", "theme-picker", "open"],
),
(
ActionKind::SurfaceKeybindingsOpen,
vec!["warpctrl", "surface", "keybindings", "open"],
),
(
ActionKind::SurfaceWarpDriveOpen,
vec!["warpctrl", "surface", "warp-drive", "open"],
),
(
ActionKind::SurfaceWarpDriveToggle,
vec!["warpctrl", "surface", "warp-drive", "toggle"],
),
(
ActionKind::SurfaceResourceCenterToggle,
vec!["warpctrl", "surface", "resource-center", "toggle"],
),
(
ActionKind::SurfaceAiAssistantToggle,
vec!["warpctrl", "surface", "ai-assistant", "toggle"],
vec!["galaxyctrl", "surface", "keybindings", "open"],
),
(
ActionKind::SurfaceCodeReviewOpen,
vec!["warpctrl", "surface", "code-review", "open"],
vec!["galaxyctrl", "surface", "code-review", "open"],
),
(
ActionKind::SurfaceCodeReviewToggle,
vec!["warpctrl", "surface", "code-review", "toggle"],
vec!["galaxyctrl", "surface", "code-review", "toggle"],
),
(
ActionKind::SurfaceProjectExplorerOpen,
vec!["warpctrl", "surface", "project-explorer", "open"],
vec!["galaxyctrl", "surface", "project-explorer", "open"],
),
(
ActionKind::SurfaceGlobalSearchOpen,
vec!["warpctrl", "surface", "global-search", "open"],
),
(
ActionKind::SurfaceConversationListOpen,
vec!["warpctrl", "surface", "conversation-list", "open"],
),
(
ActionKind::SurfaceLeftPanelToggle,
vec!["warpctrl", "surface", "left-panel", "toggle"],
vec!["galaxyctrl", "surface", "global-search", "open"],
),
(
ActionKind::SurfaceRightPanelToggle,
vec!["warpctrl", "surface", "right-panel", "toggle"],
vec!["galaxyctrl", "surface", "right-panel", "toggle"],
),
(
ActionKind::SurfaceVerticalTabsOpen,
vec!["warpctrl", "surface", "vertical-tabs", "open"],
vec!["galaxyctrl", "surface", "vertical-tabs", "open"],
),
(
ActionKind::SurfaceVerticalTabsToggle,
vec!["warpctrl", "surface", "vertical-tabs", "toggle"],
),
(
ActionKind::SurfaceAgentManagementOpen,
vec!["warpctrl", "surface", "agent-management", "open"],
vec!["galaxyctrl", "surface", "vertical-tabs", "toggle"],
),
(
ActionKind::FileOpen,
vec!["warpctrl", "file", "open", "/tmp/example.txt"],
vec!["galaxyctrl", "file", "open", "/tmp/example.txt"],
),
]
}
@@ -696,16 +726,6 @@ fn parsed_action_kind(command: &ControlCommand) -> Option<ActionKind> {
SurfaceCommand::Keybindings(command) => match command {
SurfaceOpenCommand::Open(_) => Some(ActionKind::SurfaceKeybindingsOpen),
},
SurfaceCommand::WarpDrive(command) => match command {
SurfaceOpenToggleCommand::Open(_) => Some(ActionKind::SurfaceWarpDriveOpen),
SurfaceOpenToggleCommand::Toggle(_) => Some(ActionKind::SurfaceWarpDriveToggle),
},
SurfaceCommand::ResourceCenter(command) => match command {
SurfaceToggleCommand::Toggle(_) => Some(ActionKind::SurfaceResourceCenterToggle),
},
SurfaceCommand::AiAssistant(command) => match command {
SurfaceToggleCommand::Toggle(_) => Some(ActionKind::SurfaceAiAssistantToggle),
},
SurfaceCommand::CodeReview(command) => match command {
SurfaceOpenToggleCommand::Open(_) => Some(ActionKind::SurfaceCodeReviewOpen),
SurfaceOpenToggleCommand::Toggle(_) => Some(ActionKind::SurfaceCodeReviewToggle),
@@ -716,12 +736,6 @@ fn parsed_action_kind(command: &ControlCommand) -> Option<ActionKind> {
SurfaceCommand::GlobalSearch(command) => match command {
SurfaceOpenCommand::Open(_) => Some(ActionKind::SurfaceGlobalSearchOpen),
},
SurfaceCommand::ConversationList(command) => match command {
SurfaceOpenCommand::Open(_) => Some(ActionKind::SurfaceConversationListOpen),
},
SurfaceCommand::LeftPanel(command) => match command {
SurfaceToggleCommand::Toggle(_) => Some(ActionKind::SurfaceLeftPanelToggle),
},
SurfaceCommand::RightPanel(command) => match command {
SurfaceToggleCommand::Toggle(_) => Some(ActionKind::SurfaceRightPanelToggle),
},
@@ -729,9 +743,6 @@ fn parsed_action_kind(command: &ControlCommand) -> Option<ActionKind> {
SurfaceOpenToggleCommand::Open(_) => Some(ActionKind::SurfaceVerticalTabsOpen),
SurfaceOpenToggleCommand::Toggle(_) => Some(ActionKind::SurfaceVerticalTabsToggle),
},
SurfaceCommand::AgentManagement(command) => match command {
SurfaceOpenCommand::Open(_) => Some(ActionKind::SurfaceAgentManagementOpen),
},
},
ControlCommand::Completions { .. } => None,
}