998 lines
27 KiB
Rust
998 lines
27 KiB
Rust
//! Command-line interface for controlling a running local Galaxy app.
|
|
mod commands;
|
|
mod completions;
|
|
mod mcp;
|
|
mod output;
|
|
mod selectors;
|
|
use std::ffi::OsString;
|
|
use std::process::ExitCode;
|
|
|
|
use clap::{Args, CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum};
|
|
use clap_complete::aot::Shell;
|
|
use commands::{
|
|
run_action_catalog_command, run_app_command, run_appearance_command, run_capability_command,
|
|
run_file_command, run_input_command, run_instance_command, run_keybinding_command,
|
|
run_pane_command, run_session_command, run_setting_command, run_surface_command,
|
|
run_tab_command, run_terminal_command, run_theme_command, run_window_command,
|
|
};
|
|
use completions::generate_completions_to_stdout;
|
|
use mcp::run_mcp_server;
|
|
use output::write_control_error;
|
|
|
|
use crate::agent::OutputFormat;
|
|
|
|
/// Hidden flag used by the channel-specific Galaxy app binary to enter `galaxyctrl` mode.
|
|
pub const CONTROL_MODE_FLAG: &str = "--galaxyctrl";
|
|
|
|
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 = "galaxyctrl",
|
|
display_name = "galaxyctrl",
|
|
about = "Control a running local Galaxy app instance"
|
|
)]
|
|
pub struct ControlArgs {
|
|
/// Set the output format.
|
|
#[arg(
|
|
long = "output-format",
|
|
global = true,
|
|
value_enum,
|
|
default_value_t = OutputFormat::Pretty,
|
|
env = "GALAXY_OUTPUT_FORMAT"
|
|
)]
|
|
pub output_format: OutputFormat,
|
|
|
|
#[command(subcommand)]
|
|
pub command: ControlCommand,
|
|
}
|
|
|
|
/// Commands that inspect the public action catalog.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum ActionCatalogCommand {
|
|
/// List allowlisted catalog actions.
|
|
List,
|
|
|
|
/// Inspect a single allowlisted catalog action.
|
|
Inspect {
|
|
/// Canonical action name, such as `tab.create` or `surface.settings.open`.
|
|
action: String,
|
|
},
|
|
}
|
|
|
|
impl ControlArgs {
|
|
pub fn from_env() -> Self {
|
|
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 Galaxy Control arguments only when the wrapper-injected mode flag is present.
|
|
///
|
|
/// 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()))
|
|
}
|
|
|
|
/// Testable implementation of [`Self::from_control_mode_env`].
|
|
pub fn try_parse_control_mode_from<I, T>(args: I) -> Option<Result<Self, clap::Error>>
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString>,
|
|
{
|
|
let mut stripped_args = vec![OsString::from("galaxyctrl")];
|
|
let mut found_control_mode = false;
|
|
|
|
for arg in args {
|
|
let arg = arg.into();
|
|
if !found_control_mode {
|
|
if arg.to_str() == Some(CONTROL_MODE_FLAG) {
|
|
found_control_mode = true;
|
|
}
|
|
continue;
|
|
}
|
|
stripped_args.push(arg);
|
|
}
|
|
|
|
found_control_mode.then(|| Self::try_parse_from_args(stripped_args, "galaxyctrl"))
|
|
}
|
|
|
|
pub fn clap_command() -> clap::Command {
|
|
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)
|
|
}
|
|
|
|
fn try_parse_from_args<I, T>(args: I, bin_name: impl Into<String>) -> Result<Self, clap::Error>
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString> + Clone,
|
|
{
|
|
let matches = Self::clap_command_for_bin_name(bin_name).try_get_matches_from(args)?;
|
|
Self::from_arg_matches(&matches)
|
|
}
|
|
|
|
fn clap_command_for_bin_name(bin_name: impl Into<String>) -> clap::Command {
|
|
let bin_name = bin_name.into();
|
|
<Self as CommandFactory>::command()
|
|
.version(crate::version_string())
|
|
.bin_name(bin_name.clone())
|
|
.after_help(color_print::cformat!(
|
|
r#"<bold><underline>Examples:</underline></bold>
|
|
|
|
<dim>$</dim> <bold>{bin_name} instance list</bold>
|
|
|
|
<dim>$</dim> <bold>{bin_name} tab create</bold>
|
|
<dim>$</dim> <bold>{bin_name} action list</bold>
|
|
|
|
<dim>$</dim> <bold>{bin_name} action inspect surface.settings.open</bold>
|
|
|
|
<bold><underline>Learn more:</underline></bold>
|
|
* Use <bold>{bin_name} help</bold> to learn more about each command
|
|
* Use <bold>{bin_name} action list</bold> to inspect allowlisted actions
|
|
"#
|
|
))
|
|
}
|
|
}
|
|
|
|
/// Top-level `galaxyctrl` command groups.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum ControlCommand {
|
|
/// Serve the allowlisted Galaxy Control catalog over MCP stdio.
|
|
Mcp(McpArgs),
|
|
|
|
/// Inspect local Galaxy app instances.
|
|
#[command(subcommand)]
|
|
Instance(InstanceCommand),
|
|
/// Inspect and control a selected local Galaxy app.
|
|
#[command(subcommand)]
|
|
App(AppCommand),
|
|
/// Inspect local-control capabilities.
|
|
#[command(subcommand)]
|
|
Capability(CapabilityCommand),
|
|
/// Inspect public action metadata and implementation status.
|
|
#[command(subcommand)]
|
|
Action(ActionCatalogCommand),
|
|
|
|
/// Control local Galaxy windows.
|
|
#[command(subcommand)]
|
|
Window(WindowCommand),
|
|
|
|
/// Control local Galaxy tabs.
|
|
#[command(subcommand)]
|
|
Tab(TabCommand),
|
|
/// Control local Galaxy panes.
|
|
#[command(subcommand)]
|
|
Pane(PaneCommand),
|
|
|
|
/// Control local Galaxy sessions.
|
|
#[command(subcommand)]
|
|
Session(SessionCommand),
|
|
|
|
/// Edit terminal input without submitting it.
|
|
#[command(subcommand)]
|
|
Input(InputCommand),
|
|
|
|
/// Inspect, execute, and interrupt commands in existing terminal sessions.
|
|
#[command(subcommand)]
|
|
Terminal(TerminalCommand),
|
|
|
|
/// Inspect and change Galaxy themes.
|
|
#[command(subcommand)]
|
|
Theme(ThemeCommand),
|
|
|
|
/// Inspect and change Galaxy appearance.
|
|
#[command(subcommand)]
|
|
Appearance(AppearanceCommand),
|
|
|
|
/// Inspect and change allowlisted settings.
|
|
#[command(subcommand)]
|
|
Setting(SettingCommand),
|
|
|
|
/// Inspect keybinding metadata.
|
|
#[command(subcommand)]
|
|
Keybinding(KeybindingCommand),
|
|
|
|
/// Open files in Galaxy.
|
|
#[command(subcommand)]
|
|
File(FileCommand),
|
|
|
|
/// 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/galaxyctrl completions bash)
|
|
///
|
|
/// For zsh, add the following to ~/.zshrc:
|
|
/// source <(path/to/galaxyctrl completions zsh)
|
|
///
|
|
/// For fish, add the following to ~/.config/fish/config.fish:
|
|
/// path/to/galaxyctrl completions fish | source
|
|
///
|
|
/// For Powershell, add the following to $PROFILE:
|
|
/// path\to\galaxyctrl completions powershell | Out-String | Invoke-Expression
|
|
///
|
|
/// 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.
|
|
#[arg(value_enum)]
|
|
shell: Option<Shell>,
|
|
},
|
|
}
|
|
|
|
/// Commands that inspect locally discoverable Galaxy instances.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum InstanceCommand {
|
|
/// List locally discoverable Galaxy instances.
|
|
List,
|
|
|
|
/// Print app, protocol, active target, and action metadata for the selected instance.
|
|
Inspect(TargetArgs),
|
|
}
|
|
|
|
/// Commands that inspect and control the selected Galaxy app instance.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum AppCommand {
|
|
/// Check that the selected local Galaxy app responds.
|
|
Ping(TargetArgs),
|
|
|
|
/// 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 Galaxy app.
|
|
Focus(TargetArgs),
|
|
}
|
|
|
|
/// Commands that inspect public local-control capabilities.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum CapabilityCommand {
|
|
/// List allowlisted local-control capabilities.
|
|
List,
|
|
|
|
/// Inspect a single local-control capability by canonical action name.
|
|
Inspect {
|
|
/// Canonical action name, such as `tab.create` or `surface.settings.open`.
|
|
action: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum WindowCommand {
|
|
/// List windows in the selected local Galaxy app.
|
|
List(TargetArgs),
|
|
|
|
/// Inspect one window in the selected local Galaxy app.
|
|
Inspect(TargetArgs),
|
|
|
|
/// Create a new window.
|
|
Create(TabCreateArgs),
|
|
|
|
/// Focus a window.
|
|
Focus(TargetArgs),
|
|
|
|
/// Close a window.
|
|
Close(TargetArgs),
|
|
}
|
|
|
|
/// Commands that control tabs in the selected Galaxy app instance.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum TabCommand {
|
|
/// List tabs in the selected local Galaxy app.
|
|
List(TargetArgs),
|
|
|
|
/// Inspect one tab in the selected local Galaxy app.
|
|
Inspect(TargetArgs),
|
|
|
|
/// Create a new terminal tab in the active window.
|
|
Create(TabCreateArgs),
|
|
|
|
/// Activate a tab.
|
|
Activate(TabActivateArgs),
|
|
|
|
/// Move the active tab.
|
|
Move(TabMoveArgs),
|
|
|
|
/// Close tabs.
|
|
Close(TabCloseArgs),
|
|
|
|
/// Rename a tab.
|
|
Rename(RenameArgs),
|
|
|
|
/// Reset a tab name.
|
|
ResetName(TargetArgs),
|
|
|
|
/// Set or clear a tab color.
|
|
#[command(subcommand)]
|
|
Color(TabColorCommand),
|
|
}
|
|
|
|
/// Commands that control tab colors.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum TabColorCommand {
|
|
/// Set a tab color.
|
|
Set(ColorSetArgs),
|
|
|
|
/// Clear a tab color.
|
|
Clear(TargetArgs),
|
|
}
|
|
|
|
/// Commands that control local Galaxy panes.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum PaneCommand {
|
|
/// List panes in the selected local Galaxy app.
|
|
List(TargetArgs),
|
|
|
|
/// Inspect one pane in the selected local Galaxy app.
|
|
Inspect(TargetArgs),
|
|
|
|
/// Split the active pane.
|
|
Split(PaneSplitArgs),
|
|
|
|
/// Focus a pane.
|
|
Focus(TargetArgs),
|
|
|
|
/// Navigate between panes.
|
|
Navigate(PaneNavigateArgs),
|
|
|
|
/// Resize the active pane.
|
|
Resize(PaneResizeArgs),
|
|
|
|
/// Maximize the active pane.
|
|
Maximize(TargetArgs),
|
|
|
|
/// Unmaximize the active pane.
|
|
Unmaximize(TargetArgs),
|
|
|
|
/// Close the active pane.
|
|
Close(TargetArgs),
|
|
|
|
/// Rename a pane.
|
|
Rename(RenameArgs),
|
|
|
|
/// Reset a pane name.
|
|
ResetName(TargetArgs),
|
|
}
|
|
|
|
/// Commands that control local Galaxy sessions.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum SessionCommand {
|
|
/// List sessions in the selected local Galaxy app.
|
|
List(TargetArgs),
|
|
|
|
/// Inspect one session in the selected local Galaxy app.
|
|
Inspect(TargetArgs),
|
|
|
|
/// Activate a session.
|
|
Activate(TargetArgs),
|
|
|
|
/// Activate the previous session.
|
|
Previous(TargetArgs),
|
|
|
|
/// Activate the next session.
|
|
Next(TargetArgs),
|
|
|
|
/// Reopen the most recently closed session.
|
|
ReopenClosed(TargetArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum InputCommand {
|
|
/// Insert text into the input buffer without submitting it.
|
|
Insert(TextTargetArgs),
|
|
|
|
/// Replace the input buffer without submitting it.
|
|
Replace(TextTargetArgs),
|
|
}
|
|
|
|
/// Commands that control the active command in an existing terminal session.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum TerminalCommand {
|
|
/// Inspect the current active command block.
|
|
Status(TargetArgs),
|
|
|
|
/// Submit a command, but only when the target terminal is idle.
|
|
Execute(TerminalExecuteArgs),
|
|
|
|
/// Interrupt the expected active command block.
|
|
Interrupt(TerminalInterruptArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum SurfaceCommand {
|
|
/// List available and unavailable Galaxy surfaces.
|
|
List(TargetArgs),
|
|
/// Open settings surfaces.
|
|
#[command(subcommand)]
|
|
Settings(SurfaceSettingsCommand),
|
|
|
|
/// Open the command palette.
|
|
#[command(subcommand)]
|
|
CommandPalette(SurfaceQueryCommand),
|
|
|
|
/// Open command search.
|
|
#[command(subcommand)]
|
|
CommandSearch(SurfaceQueryCommand),
|
|
/// Open the theme picker.
|
|
#[command(subcommand)]
|
|
ThemePicker(SurfaceOpenCommand),
|
|
|
|
/// Open keybinding settings.
|
|
#[command(subcommand)]
|
|
Keybindings(SurfaceOpenCommand),
|
|
|
|
/// Open or toggle code review.
|
|
#[command(subcommand)]
|
|
CodeReview(SurfaceOpenToggleCommand),
|
|
|
|
/// Open the project explorer.
|
|
#[command(subcommand)]
|
|
ProjectExplorer(SurfaceOpenCommand),
|
|
|
|
/// Open global search.
|
|
#[command(subcommand)]
|
|
GlobalSearch(SurfaceOpenCommand),
|
|
|
|
/// Toggle the right panel.
|
|
#[command(subcommand)]
|
|
RightPanel(SurfaceToggleCommand),
|
|
|
|
/// Open or toggle vertical tabs.
|
|
#[command(subcommand)]
|
|
VerticalTabs(SurfaceOpenToggleCommand),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum SurfaceSettingsCommand {
|
|
/// Open Settings, optionally scoped to a page or query.
|
|
Open(PageQueryArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum SurfaceQueryCommand {
|
|
/// Open the surface with an optional seeded query.
|
|
Open(QueryArgs),
|
|
}
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum SurfaceOpenCommand {
|
|
/// Open the surface.
|
|
Open(TargetArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum SurfaceOpenToggleCommand {
|
|
/// Open the surface.
|
|
Open(TargetArgs),
|
|
|
|
/// Toggle the surface.
|
|
Toggle(TargetArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum SurfaceToggleCommand {
|
|
/// Toggle the surface.
|
|
Toggle(TargetArgs),
|
|
}
|
|
|
|
/// Commands that inspect and change Galaxy themes.
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum ThemeCommand {
|
|
/// List available themes.
|
|
List(TargetArgs),
|
|
|
|
/// Read current theme state.
|
|
Get(TargetArgs),
|
|
|
|
/// Set the current theme.
|
|
Set(ThemeSetArgs),
|
|
|
|
/// Set whether Galaxy follows the system theme.
|
|
SystemSet(ThemeSystemSetArgs),
|
|
|
|
/// Set the light theme used when following the system theme.
|
|
LightSet(ThemeSetArgs),
|
|
|
|
/// Set the dark theme used when following the system theme.
|
|
DarkSet(ThemeSetArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum AppearanceCommand {
|
|
/// Read appearance state.
|
|
Get(TargetArgs),
|
|
|
|
/// Increase terminal font size.
|
|
FontSizeIncrease(TargetArgs),
|
|
|
|
/// Decrease terminal font size.
|
|
FontSizeDecrease(TargetArgs),
|
|
|
|
/// Reset terminal font size.
|
|
FontSizeReset(TargetArgs),
|
|
|
|
/// Increase UI zoom.
|
|
ZoomIncrease(TargetArgs),
|
|
|
|
/// Decrease UI zoom.
|
|
ZoomDecrease(TargetArgs),
|
|
|
|
/// Reset UI zoom.
|
|
ZoomReset(TargetArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum SettingCommand {
|
|
/// List allowlisted settings.
|
|
List(NamespaceTargetArgs),
|
|
|
|
/// Read one allowlisted setting.
|
|
Get(SettingGetArgs),
|
|
|
|
/// Set one allowlisted setting.
|
|
Set(SettingSetArgs),
|
|
|
|
/// Toggle one allowlisted boolean setting.
|
|
Toggle(SettingToggleArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum KeybindingCommand {
|
|
/// List keybindings.
|
|
List(TargetArgs),
|
|
|
|
/// Read one keybinding by name.
|
|
Get(KeybindingGetArgs),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Subcommand)]
|
|
pub enum FileCommand {
|
|
/// Open a file in Galaxy.
|
|
Open(FileOpenArgs),
|
|
}
|
|
|
|
/// Exact selectors for a target within the selected Galaxy instance.
|
|
#[derive(Debug, Clone, Args, Default)]
|
|
pub struct TargetArgs {
|
|
/// 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 Galaxy process id.
|
|
#[arg(long = "pid", conflicts_with = "instance")]
|
|
pub pid: Option<u32>,
|
|
|
|
/// Target the active window or an opaque window id.
|
|
#[arg(long = "window", conflicts_with_all = ["window_index", "window_title"])]
|
|
pub window: Option<String>,
|
|
|
|
/// Target a window by scoped index when the handler supports it.
|
|
#[arg(long = "window-index", conflicts_with_all = ["window", "window_title"])]
|
|
pub window_index: Option<u32>,
|
|
|
|
/// Target a window by exact title when the handler supports it.
|
|
#[arg(long = "window-title", conflicts_with_all = ["window", "window_index"])]
|
|
pub window_title: Option<String>,
|
|
|
|
/// Target the active tab or an opaque tab id.
|
|
#[arg(long = "tab", conflicts_with_all = ["tab_index", "tab_title"])]
|
|
pub tab: Option<String>,
|
|
|
|
/// Target a tab by scoped index when the handler supports it.
|
|
#[arg(long = "tab-index", conflicts_with_all = ["tab", "tab_title"])]
|
|
pub tab_index: Option<u32>,
|
|
|
|
/// Target a tab by exact title when the handler supports it.
|
|
#[arg(long = "tab-title", conflicts_with_all = ["tab", "tab_index"])]
|
|
pub tab_title: Option<String>,
|
|
|
|
/// Target the active pane or an opaque pane id.
|
|
#[arg(long = "pane", conflicts_with = "pane_index")]
|
|
pub pane: Option<String>,
|
|
|
|
/// Target a pane by scoped index when the handler supports it.
|
|
#[arg(long = "pane-index", conflicts_with = "pane")]
|
|
pub pane_index: Option<u32>,
|
|
|
|
/// Target the active session or an opaque session id.
|
|
#[arg(long = "session")]
|
|
pub session: Option<String>,
|
|
}
|
|
|
|
/// Options for serving Galaxy Control over MCP stdio.
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct McpArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
/// Expose only pane-pinned terminal tools suitable for an external agent.
|
|
#[arg(long = "agent-safe", hide = true)]
|
|
pub agent_safe: bool,
|
|
|
|
/// Permit an agent-safe MCP client to execute a command in the delegated terminal.
|
|
#[arg(long = "allow-terminal-execute", hide = true, requires = "agent_safe")]
|
|
pub allow_terminal_execute: bool,
|
|
|
|
/// Permit an agent-safe MCP client to interrupt the delegated terminal.
|
|
#[arg(
|
|
long = "allow-terminal-interrupt",
|
|
hide = true,
|
|
requires = "agent_safe"
|
|
)]
|
|
pub allow_terminal_interrupt: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct TabCreateArgs {
|
|
#[arg(long = "type", value_enum)]
|
|
pub tab_type: Option<CliTabType>,
|
|
|
|
#[arg(long = "shell")]
|
|
pub shell: Option<String>,
|
|
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct TabActivateArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
#[arg(long = "previous", conflicts_with_all = ["next", "last"])]
|
|
pub previous: bool,
|
|
|
|
#[arg(long = "next", conflicts_with_all = ["previous", "last"])]
|
|
pub next: bool,
|
|
|
|
#[arg(long = "last", conflicts_with_all = ["previous", "next"])]
|
|
pub last: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct TabMoveArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
#[arg(long = "direction", value_enum)]
|
|
pub direction: CliTabMoveDirection,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct TabCloseArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
#[arg(long = "active", conflicts_with_all = ["others", "right_of"])]
|
|
pub active: bool,
|
|
|
|
#[arg(long = "others", conflicts_with_all = ["active", "right_of"])]
|
|
pub others: bool,
|
|
|
|
#[arg(long = "right-of", conflicts_with_all = ["active", "others"])]
|
|
pub right_of: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct PaneSplitArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
#[arg(long = "direction", value_enum)]
|
|
pub direction: CliCardinalDirection,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct PaneNavigateArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
#[arg(long = "direction", value_enum)]
|
|
pub direction: CliDirection,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct PaneResizeArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
#[arg(long = "direction", value_enum)]
|
|
pub direction: CliCardinalDirection,
|
|
|
|
#[arg(long = "amount")]
|
|
pub amount: Option<u32>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct TextTargetArgs {
|
|
pub text: String,
|
|
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct TerminalExecuteArgs {
|
|
/// Command text to submit to the target terminal.
|
|
pub command: String,
|
|
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct TerminalInterruptArgs {
|
|
/// Exact active block ID returned by `terminal status`.
|
|
#[arg(long = "block-id")]
|
|
pub block_id: String,
|
|
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct PageQueryArgs {
|
|
#[arg(long = "page")]
|
|
pub page: Option<String>,
|
|
|
|
#[arg(long = "query")]
|
|
pub query: Option<String>,
|
|
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct QueryArgs {
|
|
#[arg(long = "query")]
|
|
pub query: Option<String>,
|
|
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct FileOpenArgs {
|
|
pub path: String,
|
|
|
|
#[arg(long = "line")]
|
|
pub line: Option<u32>,
|
|
|
|
#[arg(long = "column")]
|
|
pub column: Option<u32>,
|
|
|
|
#[arg(long = "new-tab")]
|
|
pub new_tab: bool,
|
|
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct RenameArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
pub title: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct ColorSetArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
pub color: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct ThemeSetArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
pub name: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct ThemeSystemSetArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
#[arg(action = clap::ArgAction::Set)]
|
|
pub enabled: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct SettingSetArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
pub key: String,
|
|
|
|
pub value: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct SettingToggleArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
pub key: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct NamespaceTargetArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
#[arg(long = "namespace")]
|
|
pub namespace: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct SettingGetArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
/// Allowlisted setting key.
|
|
pub key: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Args)]
|
|
pub struct KeybindingGetArgs {
|
|
#[command(flatten)]
|
|
pub target: TargetArgs,
|
|
|
|
/// Keybinding action name.
|
|
pub name: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
|
|
pub enum CliTabType {
|
|
Terminal,
|
|
Agent,
|
|
Default,
|
|
}
|
|
|
|
impl From<CliTabType> for local_control::protocol::TabType {
|
|
fn from(value: CliTabType) -> Self {
|
|
match value {
|
|
CliTabType::Terminal => Self::Terminal,
|
|
CliTabType::Agent => Self::Agent,
|
|
CliTabType::Default => Self::Default,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
|
|
pub enum CliCardinalDirection {
|
|
Left,
|
|
Right,
|
|
Up,
|
|
Down,
|
|
}
|
|
|
|
impl From<CliCardinalDirection> for local_control::protocol::Direction {
|
|
fn from(value: CliCardinalDirection) -> Self {
|
|
match value {
|
|
CliCardinalDirection::Left => Self::Left,
|
|
CliCardinalDirection::Right => Self::Right,
|
|
CliCardinalDirection::Up => Self::Up,
|
|
CliCardinalDirection::Down => Self::Down,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
|
|
pub enum CliDirection {
|
|
Left,
|
|
Right,
|
|
Up,
|
|
Down,
|
|
Previous,
|
|
Next,
|
|
}
|
|
|
|
impl From<CliDirection> for local_control::protocol::Direction {
|
|
fn from(value: CliDirection) -> Self {
|
|
match value {
|
|
CliDirection::Left => Self::Left,
|
|
CliDirection::Right => Self::Right,
|
|
CliDirection::Up => Self::Up,
|
|
CliDirection::Down => Self::Down,
|
|
CliDirection::Previous => Self::Previous,
|
|
CliDirection::Next => Self::Next,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
|
|
pub enum CliTabMoveDirection {
|
|
Left,
|
|
Right,
|
|
Previous,
|
|
Next,
|
|
}
|
|
|
|
impl From<CliTabMoveDirection> for local_control::protocol::Direction {
|
|
fn from(value: CliTabMoveDirection) -> Self {
|
|
match value {
|
|
CliTabMoveDirection::Left => Self::Left,
|
|
CliTabMoveDirection::Right => Self::Right,
|
|
CliTabMoveDirection::Previous => Self::Previous,
|
|
CliTabMoveDirection::Next => Self::Next,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn run(args: ControlArgs) -> ExitCode {
|
|
ExitCode::from(run_exit_code(args))
|
|
}
|
|
|
|
pub fn run_and_exit(args: ControlArgs) -> ! {
|
|
std::process::exit(i32::from(run_exit_code(args)))
|
|
}
|
|
|
|
fn run_exit_code(args: ControlArgs) -> u8 {
|
|
let output_format = args.output_format;
|
|
match run_inner(args) {
|
|
Ok(()) => 0,
|
|
Err(error) => {
|
|
if let Err(write_error) = write_control_error(&error, output_format) {
|
|
eprintln!(
|
|
"error: failed to render local-control error: {}",
|
|
write_error.message
|
|
);
|
|
}
|
|
1
|
|
}
|
|
}
|
|
}
|
|
|
|
fn run_inner(args: ControlArgs) -> Result<(), local_control::protocol::ControlError> {
|
|
let output_format = args.output_format;
|
|
match args.command {
|
|
ControlCommand::Mcp(args) => run_mcp_server(args),
|
|
ControlCommand::Instance(command) => run_instance_command(command, output_format),
|
|
ControlCommand::App(command) => run_app_command(command, output_format),
|
|
ControlCommand::Capability(command) => run_capability_command(command, output_format),
|
|
ControlCommand::Action(command) => run_action_catalog_command(command, output_format),
|
|
ControlCommand::Window(command) => run_window_command(command, output_format),
|
|
ControlCommand::Tab(command) => run_tab_command(command, output_format),
|
|
ControlCommand::Pane(command) => run_pane_command(command, output_format),
|
|
ControlCommand::Session(command) => run_session_command(command, output_format),
|
|
ControlCommand::Input(command) => run_input_command(command, output_format),
|
|
ControlCommand::Terminal(command) => run_terminal_command(command, output_format),
|
|
ControlCommand::Theme(command) => run_theme_command(command, output_format),
|
|
ControlCommand::Appearance(command) => run_appearance_command(command, output_format),
|
|
ControlCommand::Setting(command) => run_setting_command(command, output_format),
|
|
ControlCommand::Keybinding(command) => run_keybinding_command(command, output_format),
|
|
ControlCommand::File(command) => run_file_command(command, output_format),
|
|
ControlCommand::Surface(command) => run_surface_command(command, output_format),
|
|
ControlCommand::Completions { shell } => generate_completions_to_stdout(shell),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) use commands::render_human_readable_for_test;
|
|
#[cfg(test)]
|
|
pub(crate) use completions::generate_completion_string;
|
|
#[cfg(test)]
|
|
pub(crate) use output::ErrorSummary;
|
|
|
|
#[cfg(test)]
|
|
#[path = "../local_control_tests.rs"]
|
|
mod tests;
|