use clap::ArgGroup;
use clap::Args;
use clap::Parser;
use clap::Subcommand;
use clap::ValueEnum;
use clap::ValueHint;
use std::collections::HashMap;
use std::num::NonZeroU16;
use std::path::PathBuf;
pub use crate::adapters::presenter::OutputFormat;
use crate::app::attach::DetachKeys;
use crate::domain::TerminalSize;
const AFTER_HELP: &str =
"Use --help for full details and examples. Use --format json for machine-readable output.";
const LONG_ABOUT: &str = "\
Drive TUI (text UI) applications programmatically or interactively.\n\
\n\
Common flow: run -> screenshot -> press/type/scroll -> wait -> kill.\n\
Use --format json for automation-friendly output.\n\
\n\
Supported platforms: Unix-like systems only (Linux, macOS, and environments\n\
with PTYs, Unix domain sockets, and POSIX signals).";
const AFTER_LONG_HELP: &str = r#"WORKFLOW:
1. Run a TUI application
2. View the screenshot
3. Interact with keys/text or scroll
4. Wait for UI changes
5. Kill the session when done
OUTPUT:
--format json Machine-readable JSON (recommended for automation)
--format text Human-readable text (default)
CONFIGURATION:
AGENT_TUI_NO_INPUT Disable prompts and interactive TTY behavior (default: false)
AGENT_TUI_TRANSPORT IPC transport (unix or ws; default: unix)
AGENT_TUI_WS_ADDR Remote WS-RPC target when transport is ws (e.g. ws://host:port/ws)
AGENT_TUI_DETACH_KEYS Detach keys for `sessions attach` (default: Ctrl-P Ctrl-B)
AGENT_TUI_WS_LISTEN Daemon WS bind address (default: 127.0.0.1:0)
AGENT_TUI_WS_ALLOW_REMOTE Allow non-loopback WS bind (default: false)
AGENT_TUI_WS_STATE Daemon WS state file path (default: ~/.agent-tui/api.json)
AGENT_TUI_WS_DISABLED Disable daemon WS server (default: false)
AGENT_TUI_WS_MAX_CONNECTIONS Max WebSocket connections (default: 32)
AGENT_TUI_WS_QUEUE WS outbound queue size (default: 128)
AGENT_TUI_SESSION_STORE Session metadata log path (default: ~/.agent-tui/sessions.jsonl)
AGENT_TUI_LOG Log file path (optional)
AGENT_TUI_LOG_FORMAT Log format (text or json; default: text)
AGENT_TUI_LOG_STREAM Log output stream (stderr or stdout; default: stderr)
AGENT_TUI_UI_URL Same-origin UI URL or path override for live preview opening (optional)
AGENT_TUI_UI_MODE UI mode override (optional)
AGENT_TUI_UI_PORT UI port override (optional)
AGENT_TUI_UI_ROOT UI root path override (optional)
AGENT_TUI_UI_STATE UI state file path (optional)
EXAMPLES:
# Start and interact with a TUI app
agent-tui run "npx create-next-app"
agent-tui screenshot
agent-tui type "my-project" # Type text
agent-tui press Ctrl+M # Submit the current input
agent-tui wait "success"
agent-tui kill
# Navigate menus efficiently
agent-tui run htop
agent-tui press F10
agent-tui press ArrowDown ArrowDown Enter
# Scroll using directional terminal input
agent-tui scroll down
agent-tui scroll up 5
PLATFORM SUPPORT:
Supported: Linux, macOS, and other Unix-like systems with PTYs,
Unix domain sockets, and POSIX signals.
Unsupported: Windows and non-Unix runtimes.
"#;
fn parse_terminal_cols(value: &str) -> Result<u16, String> {
let cols = value
.parse::<u16>()
.map_err(|err| format!("invalid column count '{value}': {err}"))?;
if cols < TerminalSize::MIN_COLS {
return Err(format!(
"columns must be at least {}",
TerminalSize::MIN_COLS
));
}
if cols > TerminalSize::MAX_COLS {
return Err(format!(
"columns must be at most {}",
TerminalSize::MAX_COLS
));
}
Ok(cols)
}
fn parse_terminal_rows(value: &str) -> Result<u16, String> {
let rows = value
.parse::<u16>()
.map_err(|err| format!("invalid row count '{value}': {err}"))?;
if rows < TerminalSize::MIN_ROWS {
return Err(format!("rows must be at least {}", TerminalSize::MIN_ROWS));
}
if rows > TerminalSize::MAX_ROWS {
return Err(format!("rows must be at most {}", TerminalSize::MAX_ROWS));
}
Ok(rows)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnvAssignment {
pub key: String,
pub value: String,
}
fn parse_env_assignment(value: &str) -> Result<EnvAssignment, String> {
let Some((key, env_value)) = value.split_once('=') else {
return Err("expected KEY=VALUE".to_string());
};
if key.is_empty() {
return Err("environment variable name cannot be empty".to_string());
}
if key.contains('\0') || env_value.contains('\0') {
return Err("environment variables cannot contain NUL bytes".to_string());
}
Ok(EnvAssignment {
key: key.to_string(),
value: env_value.to_string(),
})
}
pub(crate) fn env_assignments_to_map(
assignments: Vec<EnvAssignment>,
) -> Option<HashMap<String, String>> {
if assignments.is_empty() {
return None;
}
Some(
assignments
.into_iter()
.map(|assignment| (assignment.key, assignment.value))
.collect(),
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum CompletionShell {
Bash,
Zsh,
Fish,
Elvish,
}
impl CompletionShell {
pub const fn clap_shell(self) -> clap_complete::Shell {
match self {
Self::Bash => clap_complete::Shell::Bash,
Self::Zsh => clap_complete::Shell::Zsh,
Self::Fish => clap_complete::Shell::Fish,
Self::Elvish => clap_complete::Shell::Elvish,
}
}
}
#[derive(Parser)]
#[command(name = "agent-tui")]
#[command(author, version, propagate_version = true)]
#[command(about = "CLI tool for AI agents to interact with TUI applications")]
#[command(long_about = LONG_ABOUT)]
#[command(after_help = AFTER_HELP)]
#[command(after_long_help = AFTER_LONG_HELP)]
#[command(subcommand_required = true, arg_required_else_help = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(
short,
long,
global = true,
value_name = "ID",
help_heading = "Session Options"
)]
pub session: Option<String>,
#[arg(
short,
long,
global = true,
value_enum,
value_name = "FORMAT",
default_value_t = OutputFormat::Text,
help_heading = "Output Options"
)]
pub format: OutputFormat,
#[arg(long, global = true, help_heading = "Output Options")]
pub json: bool,
#[arg(
long,
global = true,
env = "NO_COLOR",
value_parser = clap::builder::BoolishValueParser::new(),
help_heading = "Output Options"
)]
pub no_color: bool,
#[arg(
long,
global = true,
env = "AGENT_TUI_NO_INPUT",
value_parser = clap::builder::BoolishValueParser::new(),
help_heading = "Interaction Options"
)]
pub no_input: bool,
}
impl Cli {
pub fn effective_format(&self) -> OutputFormat {
if self.json {
OutputFormat::Json
} else {
self.format
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum LegacyActionOperation {
Click,
Fill(String),
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct LegacyActionInvocation {
pub operation: LegacyActionOperation,
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct LegacyActionParseError {
selector: String,
operation: String,
}
impl LegacyActionParseError {
pub fn selector(&self) -> &str {
&self.selector
}
pub fn operation(&self) -> &str {
&self.operation
}
}
fn legacy_action_parse_error(
selector: impl Into<String>,
operation: impl Into<String>,
) -> LegacyActionParseError {
let selector = selector.into();
let operation = operation.into();
LegacyActionParseError {
selector: if selector.is_empty() {
"<missing>".to_string()
} else {
selector
},
operation: if operation.is_empty() {
"<missing>".to_string()
} else {
operation
},
}
}
pub(crate) fn parse_legacy_action_invocation(
form: &[String],
) -> Result<LegacyActionInvocation, LegacyActionParseError> {
let [selector, operation_name, rest @ ..] = form else {
let selector = form.first().map(String::as_str).unwrap_or("<missing>");
return Err(legacy_action_parse_error(selector, "missing"));
};
match operation_name.as_str() {
"click" if rest.is_empty() => Ok(LegacyActionInvocation {
operation: LegacyActionOperation::Click,
}),
"fill" if !rest.is_empty() => Ok(LegacyActionInvocation {
operation: LegacyActionOperation::Fill(rest.join(" ")),
}),
_ => Err(legacy_action_parse_error(selector, operation_name)),
}
}
#[derive(Debug, Subcommand)]
pub enum Commands {
#[command(long_about = "\
Run a new TUI application in a virtual terminal.
Creates a new PTY session with the specified command and returns a session ID.
The session runs in the background and can be interacted with using other commands.
Use `--` before COMMAND args that start with `-` (e.g., `run -- vim -n`).")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui run bash
agent-tui run --env FOO=bar --env BAZ=qux bash
agent-tui run htop
agent-tui run \"npx create-next-app\"
agent-tui run vim -- file.txt
agent-tui run --cols 80 --rows 24 nano")]
Run {
#[arg(value_name = "COMMAND", value_hint = ValueHint::CommandName)]
command: String,
#[arg(trailing_var_arg = true, value_name = "ARG")]
args: Vec<String>,
#[arg(short = 'd', long, value_name = "DIR", value_hint = ValueHint::DirPath)]
cwd: Option<PathBuf>,
#[arg(
long = "env",
value_name = "KEY=VALUE",
value_parser = parse_env_assignment,
help_heading = "Environment"
)]
env: Vec<EnvAssignment>,
#[arg(
long,
default_value_t = 120,
value_name = "COLS",
value_parser = parse_terminal_cols,
help_heading = "Terminal Size"
)]
cols: u16,
#[arg(
long,
default_value_t = 40,
value_name = "ROWS",
value_parser = parse_terminal_rows,
help_heading = "Terminal Size"
)]
rows: u16,
},
#[command(long_about = "\
View the current screenshot state.
Returns the current terminal screenshot content.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui screenshot # Screenshot with terminal colors/styles
agent-tui screenshot --retain-ansi # Explicitly preserve terminal colors/styles
agent-tui screenshot --strip-ansi # Plain text without colors
LEGACY COMPATIBILITY:
agent-tui screenshot -e # Deprecated; returns the standard screenshot
agent-tui screenshot -a # Deprecated; returns the standard screenshot
agent-tui screenshot --interactive-only # Deprecated; returns the standard screenshot")]
Screenshot {
#[arg(long, value_name = "REGION", help_heading = "Filtering")]
region: Option<String>,
#[arg(long, conflicts_with = "retain_ansi", help_heading = "Output Options")]
strip_ansi: bool,
#[arg(long, conflicts_with = "strip_ansi", help_heading = "Output Options")]
retain_ansi: bool,
#[arg(long, help_heading = "Output Options")]
include_cursor: bool,
#[arg(short = 'e', help_heading = "Legacy Compatibility")]
legacy_element: bool,
#[arg(short = 'a', help_heading = "Legacy Compatibility")]
legacy_accessibility: bool,
#[arg(long = "interactive-only", help_heading = "Legacy Compatibility")]
legacy_interactive_only: bool,
},
#[command(long_about = "\
Deprecated compatibility command for old selector-based action workflows.
Use current terminal commands (`press`, `type`, and `scroll`) for new scripts.")]
#[command(after_long_help = "\
SUPPORTED COMPATIBILITY FORMS:
agent-tui action <selector> click # Sends Enter with `agent-tui press Enter`
agent-tui action <selector> fill <text> # Types text with `agent-tui type <text>`
Unsupported selector actions return a compatibility error with migration guidance.")]
Action {
#[arg(value_name = "FORM", required = true, num_args = 1.., allow_hyphen_values = true)]
form: Vec<String>,
},
#[command(long_about = "\
Resize the current session terminal.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui resize --cols 120 --rows 40")]
Resize {
#[arg(long, value_name = "COLS", value_parser = parse_terminal_cols)]
cols: u16,
#[arg(long, value_name = "ROWS", value_parser = parse_terminal_rows)]
rows: u16,
},
#[command(long_about = "\
Restart the current session command, creating a new session.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui restart --yes
agent-tui --session abc123 restart --dry-run")]
Restart {
#[arg(long)]
dry_run: bool,
#[arg(short = 'y', long)]
yes: bool,
},
#[command(after_long_help = "\
NOTES:
--hold/--release require a single modifier key (Ctrl, Alt, Shift, Meta)
EXAMPLES:
agent-tui press Enter
agent-tui press Ctrl+C
agent-tui press ArrowDown ArrowDown Enter
agent-tui press Shift --hold
agent-tui press Shift --release")]
Press {
#[arg(required = true, value_name = "KEY")]
keys: Vec<String>,
#[arg(long, conflicts_with = "release", help_heading = "Modifiers")]
hold: bool,
#[arg(long, conflicts_with = "hold", help_heading = "Modifiers")]
release: bool,
},
#[command(long_about = "\
Type literal text character by character.
Pass `-` to read the text payload from stdin in non-interactive pipelines.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui type \"hello world\"
agent-tui type \"user@example.com\"
printf 'project-name' | agent-tui type -")]
Type {
#[arg(value_name = "TEXT", allow_hyphen_values = true)]
text: String,
},
#[command(long_about = "\
Legacy alias for `agent-tui type`.
Use `agent-tui type` for new scripts.")]
Input {
#[arg(value_name = "TEXT", allow_hyphen_values = true)]
text: String,
},
#[command(long_about = "\
Send repeated directional input to the terminal.
This is a thin convenience wrapper over terminal keys:
up -> ArrowUp
down -> ArrowDown
left -> ArrowLeft
right -> ArrowRight")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui scroll down
agent-tui scroll up 10
agent-tui scroll right 3")]
Scroll {
#[arg(value_enum, value_name = "DIRECTION")]
direction: ScrollDirection,
#[arg(default_value_t = NonZeroU16::MIN, value_name = "AMOUNT")]
amount: NonZeroU16,
},
#[command(long_about = "\
Deprecated compatibility command for old element scroll workflows.
The current CLI has no element selector engine. This command does not send terminal input; use `scroll` or `press` for new scripts.")]
#[command(after_long_help = "\
SUPPORTED COMPATIBILITY FORMS:
agent-tui scroll-into-view <selector> # No-op compatibility success
Unsupported selector options return a compatibility error with migration guidance.")]
ScrollIntoView {
#[arg(value_name = "FORM", required = true, num_args = 1.., allow_hyphen_values = true)]
form: Vec<String>,
},
#[command(long_about = "\
Wait for a condition to be met before continuing.
Waits for text to appear/disappear or the screenshot to stabilize.
Returns success if the condition is met within the timeout period.
WAIT CONDITIONS:
<text> Wait for text to appear on screenshot
--stable Wait for screenshot to stop changing
-g, --gone Modifier: wait for text to disappear
-e <ref> Deprecated: treats element ref as literal text
ASSERT MODE:
--assert Exit with code 0 if condition met, 75 if timeout.
Without --assert, always exit 0 (timeout still reported).")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui wait \"Continue\" # Wait for text
agent-tui wait --stable # Wait for screenshot stability
agent-tui wait \"Loading\" --gone # Wait for text to disappear
agent-tui wait -t 5000 \"Done\" # 5 second timeout")]
Wait {
#[command(flatten)]
params: WaitParams,
},
#[command(after_long_help = "\
EXAMPLES:
agent-tui kill --yes
agent-tui --session abc123 kill --dry-run")]
Kill {
#[arg(long)]
dry_run: bool,
#[arg(short = 'y', long)]
yes: bool,
},
#[command(long_about = "\
Manage sessions - list, show details, attach, switch active, or cleanup.
By default, lists all active sessions.
MODES:
list List active sessions (default)
show <id> Show details for a session
attach Attach with TTY (defaults to --session or active)
switch <id> Set the active session
cleanup [--all] Remove dead/orphaned sessions")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui sessions # List sessions
agent-tui sessions list # List sessions (explicit)
agent-tui sessions show abc123 # Show session details
agent-tui sessions attach # Attach to active session (TTY)
agent-tui -s abc123 sessions attach # Attach to session by id (TTY)
agent-tui sessions switch abc123 # Set active session
agent-tui -s abc123 sessions attach -T # Attach without TTY (stream output only)
agent-tui sessions attach --detach-keys 'ctrl-]' # Custom detach sequence
agent-tui sessions cleanup --yes # Remove dead sessions
agent-tui sessions cleanup --all --dry-run # Preview removing all sessions")]
#[command(after_help = "Default action: list (same as `sessions list`).")]
Sessions {
#[command(subcommand)]
command: Option<SessionsCommand>,
},
#[command(long_about = "\
Show the local daemon's live preview WebSocket endpoints.
The daemon serves a built-in web UI at /ui and exposes JSON-RPC over WebSocket at /ws.
Use this command to print WS/UI URLs so external frontends can connect.
This command always inspects the local daemon and does not use AGENT_TUI_TRANSPORT.
CONFIGURATION:
AGENT_TUI_WS_LISTEN Bind address (default: 127.0.0.1:0)
AGENT_TUI_WS_ALLOW_REMOTE Allow non-loopback bind (default: false)
AGENT_TUI_WS_STATE State file path (default: ~/.agent-tui/api.json)
AGENT_TUI_UI_URL Same-origin UI URL or path to open with --open (CLI appends ws/session/auto)
SECURITY:
Remote exposure is opt-in. Set AGENT_TUI_WS_ALLOW_REMOTE=1 for non-loopback binds.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui live start
agent-tui live status
agent-tui live stop")]
#[command(after_help = "Default action: info (same as `live start`).")]
Live {
#[command(subcommand)]
command: Option<LiveCommand>,
},
#[command(long_about = "\
Manage the background daemon lifecycle.
Use `daemon start` to launch in the background, `daemon run` for foreground
debugging, and `daemon status` to inspect the local daemon state.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui daemon start
agent-tui daemon status
agent-tui daemon stop --yes")]
#[command(subcommand)]
Daemon(DaemonCommand),
#[command(long_about = "\
Show detailed version information.
Shows version info for both the CLI binary and the local running daemon.
Useful for verifying CLI/daemon compatibility.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui version
agent-tui --format json version")]
Version,
#[command(long_about = "\
Show environment diagnostics.
Displays all environment variables and configuration that affect
agent-tui behavior. Useful for troubleshooting connection issues.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui env
agent-tui --format json env")]
Env,
#[command(long_about = "\
Generate or install shell completions for bash, zsh, fish, or elvish.
Runs an interactive setup by default (auto-detects your shell) and checks
whether your installed completions are up-to-date. Use --print to output the
raw completion script for scripting or redirection.
Use --no-input to disable prompts and require explicit shell selection.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui completions
agent-tui completions zsh
agent-tui completions --print bash
agent-tui completions --install fish
INSTALLATION:
# Bash - add to ~/.bashrc
source <(agent-tui completions bash --print)
# Zsh - add to ~/.zshrc
source <(agent-tui completions zsh --print)
# Fish - run once
agent-tui completions fish --print > ~/.config/fish/completions/agent-tui.fish
# Elvish - run once
agent-tui completions elvish --print > ~/.elvish/lib/agent-tui.elv")]
Completions {
#[arg(value_enum, value_name = "SHELL")]
shell: Option<CompletionShell>,
#[arg(long, conflicts_with = "install")]
print: bool,
#[arg(long, conflicts_with = "print")]
install: bool,
#[arg(short = 'y', long)]
yes: bool,
},
}
#[derive(Debug, Subcommand)]
pub enum SessionsCommand {
#[command(alias = "ls")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui sessions list
agent-tui --json sessions list")]
List,
#[command(after_long_help = "\
EXAMPLES:
agent-tui sessions show abc123
agent-tui --json sessions show abc123")]
Show {
#[arg(value_name = "ID")]
session_id: String,
},
#[command(after_long_help = "\
NOTES:
--no-input implies --no-tty for automation-safe streaming.
EXAMPLES:
agent-tui sessions attach
agent-tui -s abc123 sessions attach --no-tty
agent-tui --no-input sessions attach")]
Attach {
#[arg(short = 'T', long = "no-tty")]
no_tty: bool,
#[arg(
long = "detach-keys",
value_name = "KEYS",
env = "AGENT_TUI_DETACH_KEYS"
)]
detach_keys: Option<DetachKeys>,
},
#[command(alias = "select")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui sessions switch abc123
agent-tui sessions select abc123")]
Switch {
#[arg(value_name = "ID")]
session_id: String,
},
#[command(after_long_help = "\
EXAMPLES:
agent-tui sessions cleanup --yes
agent-tui sessions cleanup --all --dry-run")]
Cleanup {
#[arg(long)]
all: bool,
#[arg(long)]
dry_run: bool,
#[arg(short = 'y', long)]
yes: bool,
},
}
#[derive(Debug, Subcommand)]
pub enum LiveCommand {
#[command(alias = "info")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui live start
agent-tui live start --open")]
Start(LiveStartArgs),
#[command(after_long_help = "\
EXAMPLES:
agent-tui live stop
agent-tui daemon stop --yes # Stop daemon-backed live preview")]
Stop,
#[command(after_long_help = "\
EXAMPLES:
agent-tui live status
agent-tui --json live status")]
Status,
}
#[derive(Debug, Clone, Default, Args)]
pub struct LiveStartArgs {
#[arg(long)]
pub open: bool,
#[arg(long, value_name = "CMD", value_hint = ValueHint::CommandName)]
pub browser: Option<String>,
}
#[derive(Debug, Subcommand)]
#[command(subcommand_required = true, arg_required_else_help = true)]
pub enum DaemonCommand {
#[command(long_about = "\
Start the daemon process.
Starts the daemon in the background. Use `daemon run` to keep it in the
foreground.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui daemon start # Start in background")]
Start {},
#[command(long_about = "\
Run the daemon in the foreground.
This is the UNIX-style form for supervisors and local debugging when you want
the daemon attached to the current process instead of forking to the
background.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui daemon run
AGENT_TUI_WS_LISTEN=0.0.0.0:8080 agent-tui daemon run")]
Run,
#[command(long_about = "\
Stop the running daemon.
Sends SIGTERM to gracefully stop the daemon, allowing it to clean up
sessions and resources. Use --force to send SIGKILL for immediate
termination (not recommended unless daemon is unresponsive).")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui daemon stop --yes # Graceful stop
agent-tui daemon stop --force --yes # Force kill")]
Stop {
#[arg(long)]
force: bool,
#[arg(long)]
dry_run: bool,
#[arg(short = 'y', long)]
yes: bool,
},
#[command(long_about = "\
Show daemon status.
Reports whether the daemon is running, its PID, versions, and any discovered
WS/UI endpoints.
EXIT CODES (LSB init script conventions):
0 - Daemon is running
3 - Daemon is not running")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui daemon status
agent-tui --json daemon status")]
Status,
#[command(long_about = "\
Restart the daemon.
Stops the running daemon and starts a new one. Useful after updating
the agent-tui binary to ensure the daemon is running the new version.
All active sessions will be terminated during restart.")]
#[command(after_long_help = "\
EXAMPLES:
agent-tui daemon restart --yes
agent-tui daemon restart --dry-run")]
Restart {
#[arg(long)]
dry_run: bool,
#[arg(short = 'y', long)]
yes: bool,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum ScrollDirection {
Up,
Down,
Left,
Right,
}
impl ScrollDirection {
pub fn as_str(self) -> &'static str {
match self {
Self::Up => "up",
Self::Down => "down",
Self::Left => "left",
Self::Right => "right",
}
}
}
#[derive(Debug, Clone, Default, Args)]
#[command(
group = ArgGroup::new("wait_condition")
.multiple(false)
.required(true)
.args(&["text", "stable", "legacy_element"]),
group = ArgGroup::new("wait_text_condition")
.multiple(false)
.required(false)
.args(&["text", "legacy_element"])
)]
pub struct WaitParams {
#[arg(value_name = "TEXT", allow_hyphen_values = true)]
pub text: Option<String>,
#[arg(
short,
long,
default_value_t = 30_000,
value_name = "MILLIS",
help_heading = "Timing"
)]
pub timeout: u64,
#[arg(long, group = "wait_condition", help_heading = "Wait Condition")]
pub stable: bool,
#[arg(
short = 'g',
long,
requires = "wait_text_condition",
help_heading = "Wait Condition"
)]
pub gone: bool,
#[arg(
short = 'e',
value_name = "REF",
allow_hyphen_values = true,
group = "wait_condition",
help_heading = "Legacy Compatibility"
)]
pub legacy_element: Option<String>,
#[arg(long, help_heading = "Behavior")]
pub assert: bool,
}
#[cfg(test)]
#[path = "commands_tests.rs"]
mod tests;