1use clap::Parser;
2use clap::Subcommand;
3use clap::ValueEnum;
4pub use clap_complete::Shell;
5
6const LONG_ABOUT: &str = r#"agent-tui enables AI agents to interact with TUI (Text User Interface) applications.
7
8WORKFLOW:
9 1. Run a TUI application
10 2. Take snapshots to see the screen and detect elements
11 3. Interact using fill, click, key commands
12 4. Wait for UI changes
13 5. Kill the session when done
14
15ELEMENT REFS:
16 Element refs are simple sequential identifiers like @e1, @e2, @e3 that
17 you can use to interact with detected UI elements. Run 'agent-tui snap -e'
18 to see available elements and their refs.
19
20 @e1, @e2, @e3, ... - Elements in document order (top-to-bottom, left-to-right)
21
22 Refs reset on each snapshot. Always use the latest snapshot's refs.
23
24EXAMPLES:
25 # Start a new Next.js project wizard
26 agent-tui run "npx create-next-app"
27 agent-tui snap -e
28 agent-tui fill @e1 "my-project"
29 agent-tui key Enter
30 agent-tui wait "success"
31 agent-tui kill
32
33 # Interactive menu navigation
34 agent-tui run htop
35 agent-tui key F10
36 agent-tui snap -e
37 agent-tui click @e1
38 agent-tui kill
39
40 # Check daemon status
41 agent-tui status -v"#;
42
43#[derive(Parser)]
44#[command(name = "agent-tui")]
45#[command(author, version)]
46#[command(about = "CLI tool for AI agents to interact with TUI applications")]
47#[command(long_about = LONG_ABOUT)]
48pub struct Cli {
49 #[command(subcommand)]
50 pub command: Commands,
51
52 #[arg(short, long, global = true)]
54 pub session: Option<String>,
55
56 #[arg(short, long, global = true, default_value = "text")]
58 pub format: OutputFormat,
59
60 #[arg(long, global = true)]
62 pub json: bool,
63
64 #[arg(long, global = true, env = "NO_COLOR")]
66 pub no_color: bool,
67
68 #[arg(short, long, global = true)]
70 pub verbose: bool,
71}
72
73impl Cli {
74 pub fn effective_format(&self) -> OutputFormat {
76 if self.json {
77 OutputFormat::Json
78 } else {
79 self.format
80 }
81 }
82}
83
84#[derive(Debug, Subcommand)]
85pub enum Commands {
86 #[command(name = "run")]
88 #[command(long_about = r#"Run a new TUI application in a virtual terminal.
89
90Creates a new PTY session with the specified command and returns a session ID.
91The session runs in the background and can be interacted with using other commands.
92
93EXAMPLES:
94 agent-tui run bash
95 agent-tui run htop
96 agent-tui run "npx create-next-app"
97 agent-tui run vim -- file.txt
98 agent-tui run --cols 80 --rows 24 nano"#)]
99 Run {
100 command: String,
102
103 #[arg(trailing_var_arg = true)]
105 args: Vec<String>,
106
107 #[arg(short = 'd', long)]
109 cwd: Option<String>,
110
111 #[arg(long, default_value = "120")]
113 cols: u16,
114
115 #[arg(long, default_value = "40")]
117 rows: u16,
118 },
119
120 #[command(name = "snap")]
122 #[command(long_about = r#"Take a snapshot of the current screen state.
123
124Returns the current terminal screen content and optionally detects
125interactive UI elements like buttons, inputs, and menus.
126
127Element detection uses the Visual Object Model (VOM) which identifies
128UI components based on visual styling (colors, backgrounds) rather than
129text patterns. This provides reliable detection across different TUI frameworks.
130
131ACCESSIBILITY TREE FORMAT (-a):
132 Returns an agent-browser style accessibility tree with refs for elements:
133 - button "Submit" [ref=e1]
134 - textbox "Search" [ref=e2]
135
136EXAMPLES:
137 agent-tui snap # Just the screen
138 agent-tui snap -e # Screen + detected elements
139 agent-tui snap -a # Accessibility tree format
140 agent-tui snap -a --interactive-only # Only interactive elements
141 agent-tui snap --strip-ansi # Plain text without colors"#)]
142 Snap {
143 #[arg(short = 'i', long)]
145 elements: bool,
146
147 #[arg(short = 'a', long)]
149 accessibility: bool,
150
151 #[arg(long)]
153 interactive_only: bool,
154
155 #[arg(long)]
157 region: Option<String>,
158
159 #[arg(long)]
161 strip_ansi: bool,
162
163 #[arg(long)]
165 include_cursor: bool,
166 },
167
168 Click {
170 #[arg(name = "ref")]
172 element_ref: String,
173
174 #[arg(short = '2', long)]
176 double: bool,
177 },
178
179 Fill {
181 #[arg(name = "ref")]
183 element_ref: String,
184
185 value: String,
187 },
188
189 #[command(long_about = r#"Send keystrokes or type text.
191
192Unified command for all keyboard input. Can press a single key, type text,
193or hold/release modifier keys.
194
195SUPPORTED KEYS:
196 Enter, Tab, Escape, Backspace, Delete
197 ArrowUp, ArrowDown, ArrowLeft, ArrowRight
198 Home, End, PageUp, PageDown
199 F1-F12
200
201MODIFIERS:
202 Ctrl+<key> - Control modifier (e.g., Ctrl+C, Ctrl+A)
203 Alt+<key> - Alt modifier (e.g., Alt+F4)
204 Shift+<key> - Shift modifier
205
206EXAMPLES:
207 agent-tui key Enter # Press Enter
208 agent-tui key Ctrl+C # Press Ctrl+C
209 agent-tui key --type "hello" # Type text char-by-char
210 agent-tui key -t "hello" # Short form for typing
211 agent-tui key Shift --hold # Hold Shift down
212 agent-tui key Shift --release # Release Shift"#)]
213 Key {
214 #[arg(required_unless_present = "text")]
216 key: Option<String>,
217
218 #[arg(short = 't', long = "type", conflicts_with = "key")]
220 text: Option<String>,
221
222 #[arg(long, conflicts_with_all = ["text", "release"])]
224 hold: bool,
225
226 #[arg(long, conflicts_with_all = ["text", "hold"])]
228 release: bool,
229 },
230
231 #[command(long_about = r#"Wait for a condition to be met before continuing.
233
234Waits for text to appear, elements to change, or the screen to stabilize.
235Returns success if the condition is met within the timeout period.
236
237WAIT CONDITIONS:
238 <text> Wait for text to appear on screen
239 -e, --element <ref> Wait for element to appear
240 --focused <ref> Wait for element to be focused
241 --stable Wait for screen to stop changing
242 --value <ref>=<val> Wait for input to have specific value
243 -g, --gone Modifier: wait for element/text to disappear
244
245EXAMPLES:
246 agent-tui wait "Continue" # Wait for text
247 agent-tui wait -e @btn1 # Wait for element
248 agent-tui wait -e @spinner --gone # Wait for element to disappear
249 agent-tui wait "Loading" --gone # Wait for text to disappear
250 agent-tui wait --stable # Wait for screen stability
251 agent-tui wait --focused @inp1 # Wait for focus
252 agent-tui wait -t 5000 "Done" # 5 second timeout"#)]
253 Wait {
254 #[command(flatten)]
255 params: WaitParams,
256 },
257
258 Kill,
260
261 #[command(long_about = r#"Restart the TUI application.
263
264Kills the current session and restarts it with the same command.
265Equivalent to running 'kill' followed by 'run' with the original command.
266
267This is the TUI equivalent of browser's 'reload' command.
268
269EXAMPLES:
270 agent-tui restart # Restart current session
271 agent-tui restart -s htop-abc123 # Restart specific session"#)]
272 Restart,
273
274 #[command(name = "ls")]
276 Ls,
277
278 #[command(name = "status")]
280 Status {
281 #[arg(short, long)]
283 verbose: bool,
284 },
285
286 Select {
288 #[arg(name = "ref")]
290 element_ref: String,
291
292 option: String,
294 },
295
296 #[command(long_about = r#"Select multiple options in a multi-select list.
298
299This is the TUI equivalent of browser's multi-select functionality.
300Use this for lists where multiple items can be selected simultaneously.
301
302Typical multi-select interaction in TUI:
3031. Focus the list element
3042. Navigate with arrow keys
3053. Press Space to toggle selection for each option
306
307EXAMPLES:
308 agent-tui multiselect @e3 "Option 1" "Option 3"
309 agent-tui multiselect @list1 red blue green"#)]
310 #[command(name = "multiselect")]
311 MultiSelect {
312 #[arg(name = "ref")]
314 element_ref: String,
315
316 #[arg(required = true)]
318 options: Vec<String>,
319 },
320
321 Scroll {
323 #[arg(value_enum, required_unless_present = "to_ref")]
325 direction: Option<ScrollDirection>,
326
327 #[arg(short, long, default_value = "5")]
329 amount: u16,
330
331 #[arg(short, long)]
333 element: Option<String>,
334
335 #[arg(long = "to")]
337 to_ref: Option<String>,
338 },
339
340 Focus {
342 #[arg(name = "ref")]
344 element_ref: String,
345 },
346
347 Clear {
349 #[arg(name = "ref")]
351 element_ref: String,
352 },
353
354 #[command(name = "selectall")]
356 SelectAll {
357 #[arg(name = "ref")]
359 element_ref: String,
360 },
361
362 #[command(long_about = r#"Count elements matching criteria.
364
365This is the TUI equivalent of browser's count command.
366Returns the number of elements matching the specified role, name, or text.
367
368EXAMPLES:
369 agent-tui count --role button
370 agent-tui count --text "Submit"
371 agent-tui count --role input --name "Email""#)]
372 Count {
373 #[arg(long)]
375 role: Option<String>,
376
377 #[arg(long)]
379 name: Option<String>,
380
381 #[arg(long)]
383 text: Option<String>,
384 },
385
386 #[command(long_about = r#"Toggle a checkbox or radio button.
388
389Use this to toggle the checked state of a checkbox or radio button.
390By default, it inverts the current state. Use --on or --off to force a specific state.
391
392EXAMPLES:
393 agent-tui toggle @e5 # Toggle current state
394 agent-tui toggle @e5 --on # Force checked (idempotent)
395 agent-tui toggle @e5 --off # Force unchecked (idempotent)"#)]
396 Toggle {
397 #[arg(name = "ref")]
399 element_ref: String,
400
401 #[arg(long, conflicts_with = "off")]
403 on: bool,
404
405 #[arg(long, conflicts_with = "on")]
407 off: bool,
408 },
409
410 #[command(subcommand)]
412 Debug(DebugCommand),
413
414 #[command(name = "record-start")]
416 #[command(hide = true)]
417 RecordStart,
418
419 #[command(name = "record-stop")]
421 #[command(hide = true)]
422 RecordStop {
423 #[arg(short, long)]
425 output: Option<String>,
426
427 #[arg(long, value_enum, default_value = "json")]
429 record_format: RecordFormat,
430 },
431
432 #[command(name = "record-status")]
434 #[command(hide = true)]
435 RecordStatus,
436
437 #[command(hide = true)]
439 Trace {
440 #[arg(short = 'n', long, default_value = "10")]
442 count: usize,
443
444 #[arg(long)]
446 start: bool,
447
448 #[arg(long)]
450 stop: bool,
451 },
452
453 #[command(hide = true)]
455 Console {
456 #[arg(short = 'n', long, default_value = "100")]
458 lines: usize,
459
460 #[arg(long)]
462 clear: bool,
463 },
464
465 #[command(hide = true)]
467 Errors {
468 #[arg(short = 'n', long, default_value = "10")]
470 count: usize,
471
472 #[arg(long)]
474 clear: bool,
475 },
476
477 #[command(long_about = r#"Resize the terminal window dimensions.
479
480Changes the number of columns and rows for the PTY. This affects how
481TUI applications render and can be useful for testing responsive layouts.
482
483EXAMPLES:
484 agent-tui resize --cols 120 --rows 40
485 agent-tui resize --cols 80 --rows 24"#)]
486 Resize {
487 #[arg(long, default_value = "120")]
489 cols: u16,
490
491 #[arg(long, default_value = "40")]
493 rows: u16,
494 },
495
496 #[command(long_about = r#"Attach to an existing session by ID.
498
499By default, makes the specified session the active session for subsequent commands.
500
501With --interactive (-i), attaches your terminal directly to the session
502for a native terminal experience. Your keystrokes go directly to the app,
503and you see its output in real-time. Press Ctrl+\ to detach.
504
505EXAMPLES:
506 agent-tui attach abc123 # Set as active session
507 agent-tui attach -i abc123 # Interactive mode (native terminal)
508 agent-tui ls # List session IDs first"#)]
509 Attach {
510 session_id: String,
512
513 #[arg(short, long)]
515 interactive: bool,
516 },
517
518 #[command(subcommand)]
520 Daemon(DaemonCommand),
521
522 #[command(long_about = r#"Show detailed version information.
524
525Shows version info for both the CLI binary and the running daemon.
526Useful for debugging and ensuring CLI/daemon compatibility.
527
528EXAMPLES:
529 agent-tui version
530 agent-tui version -f json"#)]
531 Version,
532
533 #[command(long_about = r#"Show environment diagnostics.
535
536Displays all environment variables and configuration that affect
537agent-tui behavior. Useful for debugging connection issues.
538
539EXAMPLES:
540 agent-tui env
541 agent-tui env -f json"#)]
542 Env,
543
544 #[command(long_about = r#"Assert a condition and exit with status code.
546
547Useful for automated testing and scripting. Exits with code 0 if
548the condition passes, or code 1 if it fails.
549
550CONDITIONS:
551 text:<pattern> Assert text is visible on screen
552 element:<ref> Assert element exists
553 session:<id> Assert session exists and is running
554 exit_code:<expected> Assert last process exit code (if applicable)
555
556EXAMPLES:
557 agent-tui assert text:Success
558 agent-tui assert element:@btn1
559 agent-tui assert session:main"#)]
560 Assert {
561 condition: String,
563 },
564
565 #[command(long_about = r#"Clean up stale sessions.
567
568Removes sessions that are no longer running or have been idle
569for too long. Useful for freeing resources.
570
571EXAMPLES:
572 agent-tui cleanup # Remove dead sessions
573 agent-tui cleanup --all # Remove all sessions"#)]
574 Cleanup {
575 #[arg(long)]
577 all: bool,
578 },
579
580 #[command(long_about = r#"Find elements by semantic properties.
582
583This is a semantic locator that allows finding elements without relying
584on refs that may change. Returns matching element refs.
585
586Find modes:
587 - By role and name: agent-tui find --role button --name "Submit"
588 - By text content: agent-tui find --text "Continue"
589 - By focus state: agent-tui find --focused
590 - By placeholder: agent-tui find --placeholder "Enter email"
591 - Select nth result: agent-tui find --role button --nth 1
592
593MATCHING:
594 By default, text matching is case-insensitive and matches substrings.
595 Use --exact for exact string matching.
596
597EXAMPLES:
598 agent-tui find --role button --name "Submit"
599 agent-tui find --text "Continue"
600 agent-tui find --focused
601 agent-tui find --role input
602 agent-tui find --placeholder "Search..." # Find by placeholder text
603 agent-tui find --role button --nth 1 # Get second button (0-indexed)
604 agent-tui find --text "Log" --exact # Exact match only"#)]
605 Find {
606 #[command(flatten)]
607 params: FindParams,
608 },
609
610 #[command(
612 long_about = r#"Generate shell completion scripts for bash, zsh, fish, powershell, or elvish.
613
614INSTALLATION:
615 # Bash - add to ~/.bashrc
616 source <(agent-tui completions bash)
617
618 # Zsh - add to ~/.zshrc
619 source <(agent-tui completions zsh)
620
621 # Fish - run once
622 agent-tui completions fish > ~/.config/fish/completions/agent-tui.fish
623
624 # PowerShell - add to $PROFILE
625 agent-tui completions powershell | Out-String | Invoke-Expression
626
627EXAMPLES:
628 agent-tui completions bash
629 agent-tui completions zsh > /usr/local/share/zsh/site-functions/_agent-tui"#
630 )]
631 Completions {
632 #[arg(value_enum)]
634 shell: Shell,
635 },
636}
637
638#[derive(Debug, Subcommand)]
640pub enum DaemonCommand {
641 #[command(long_about = r#"Start the daemon process.
643
644By default, starts the daemon in the background. Use --foreground to run
645in the current terminal (useful for debugging).
646
647EXAMPLES:
648 agent-tui daemon start # Start in background
649 agent-tui daemon start --foreground # Run in foreground (blocks)"#)]
650 Start {
651 #[arg(long)]
653 foreground: bool,
654 },
655
656 #[command(long_about = r#"Stop the running daemon.
658
659Sends SIGTERM to gracefully stop the daemon, allowing it to clean up
660sessions and resources. Use --force to send SIGKILL for immediate
661termination (not recommended unless daemon is unresponsive).
662
663EXAMPLES:
664 agent-tui daemon stop # Graceful stop
665 agent-tui daemon stop --force # Force kill (SIGKILL)"#)]
666 Stop {
667 #[arg(long)]
669 force: bool,
670 },
671
672 #[command(long_about = r#"Show daemon status and version information.
674
675Displays whether the daemon is running, its PID, uptime, and version.
676Also checks for version mismatch between CLI and daemon.
677
678EXAMPLES:
679 agent-tui daemon status"#)]
680 Status,
681
682 #[command(long_about = r#"Restart the daemon.
684
685Stops the running daemon and starts a new one. Useful after updating
686the agent-tui binary to ensure the daemon is running the new version.
687
688All active sessions will be terminated during restart.
689
690EXAMPLES:
691 agent-tui daemon restart"#)]
692 Restart,
693}
694
695#[derive(Debug, Subcommand)]
697pub enum DebugCommand {
698 #[command(subcommand)]
700 Record(RecordAction),
701
702 Trace {
704 #[arg(short = 'n', long, default_value = "10")]
706 count: usize,
707
708 #[arg(long)]
710 start: bool,
711
712 #[arg(long)]
714 stop: bool,
715 },
716
717 Console {
719 #[arg(short = 'n', long, default_value = "100")]
721 lines: usize,
722
723 #[arg(long)]
725 clear: bool,
726 },
727
728 Errors {
730 #[arg(short = 'n', long, default_value = "10")]
732 count: usize,
733
734 #[arg(long)]
736 clear: bool,
737 },
738
739 Env,
741}
742
743#[derive(Debug, Subcommand)]
745pub enum RecordAction {
746 Start,
748
749 Stop {
751 #[arg(short, long)]
753 output: Option<String>,
754
755 #[arg(long, value_enum, default_value = "json")]
757 format: RecordFormat,
758 },
759
760 Status,
762}
763
764#[derive(Clone, Copy, Debug, ValueEnum, Default)]
765pub enum RecordFormat {
766 #[default]
767 Json,
768 Asciicast,
769}
770
771impl RecordFormat {
772 pub fn as_str(self) -> &'static str {
773 match self {
774 RecordFormat::Json => "json",
775 RecordFormat::Asciicast => "asciicast",
776 }
777 }
778}
779
780#[derive(Clone, Copy, Debug, ValueEnum)]
781pub enum ScrollDirection {
782 Up,
783 Down,
784 Left,
785 Right,
786}
787
788impl ScrollDirection {
789 pub fn as_str(self) -> &'static str {
790 match self {
791 ScrollDirection::Up => "up",
792 ScrollDirection::Down => "down",
793 ScrollDirection::Left => "left",
794 ScrollDirection::Right => "right",
795 }
796 }
797}
798
799#[derive(Debug, Clone, Default, Parser)]
801#[command(group = clap::ArgGroup::new("wait_condition").multiple(false).args(&["element", "focused", "stable", "value"]))]
802pub struct WaitParams {
803 pub text: Option<String>,
805
806 #[arg(short, long, default_value = "30000")]
808 pub timeout: u64,
809
810 #[arg(short = 'e', long, group = "wait_condition")]
812 pub element: Option<String>,
813
814 #[arg(long, group = "wait_condition")]
816 pub focused: Option<String>,
817
818 #[arg(long, group = "wait_condition")]
820 pub stable: bool,
821
822 #[arg(long, group = "wait_condition")]
824 pub value: Option<String>,
825
826 #[arg(short = 'g', long)]
828 pub gone: bool,
829}
830
831impl WaitParams {
832 pub fn resolve_condition(&self) -> (Option<String>, Option<String>) {
834 if self.stable {
835 return (Some("stable".to_string()), None);
836 }
837
838 if let Some(ref elem) = self.element {
840 let condition = if self.gone { "not_visible" } else { "element" };
841 return (Some(condition.to_string()), Some(elem.clone()));
842 }
843
844 if let Some(ref elem) = self.focused {
845 return (Some("focused".to_string()), Some(elem.clone()));
846 }
847
848 if let Some(ref val) = self.value {
849 return (Some("value".to_string()), Some(val.clone()));
850 }
851
852 if let Some(ref txt) = self.text {
854 if self.gone {
855 return (Some("text_gone".to_string()), Some(txt.clone()));
856 }
857 }
859
860 (None, None)
861 }
862}
863
864#[derive(Debug, Clone, Default, Parser)]
866pub struct FindParams {
867 #[arg(long)]
869 pub role: Option<String>,
870
871 #[arg(long)]
873 pub name: Option<String>,
874
875 #[arg(long)]
877 pub text: Option<String>,
878
879 #[arg(long)]
881 pub placeholder: Option<String>,
882
883 #[arg(long)]
885 pub focused: bool,
886
887 #[arg(long)]
889 pub nth: Option<usize>,
890
891 #[arg(long)]
893 pub exact: bool,
894}
895
896#[derive(Clone, Copy, Debug, ValueEnum, Default, PartialEq)]
897pub enum OutputFormat {
898 #[default]
899 Text,
900 Json,
901}
902
903#[cfg(test)]
904mod tests {
905 use super::*;
906 use clap::Parser;
907
908 #[test]
910 fn test_cli_defaults() {
911 let cli = Cli::parse_from(["agent-tui", "status"]);
912 assert!(cli.session.is_none());
913 assert_eq!(cli.format, OutputFormat::Text);
914 assert!(!cli.no_color);
915 assert!(!cli.verbose);
916 }
917
918 #[test]
920 fn test_global_args() {
921 let cli = Cli::parse_from([
922 "agent-tui",
923 "--session",
924 "my-session",
925 "--format",
926 "json",
927 "--no-color",
928 "--verbose",
929 "status",
930 ]);
931 assert_eq!(cli.session, Some("my-session".to_string()));
932 assert_eq!(cli.format, OutputFormat::Json);
933 assert!(cli.no_color);
934 assert!(cli.verbose);
935 }
936
937 #[test]
939 fn test_run_defaults() {
940 let cli = Cli::parse_from(["agent-tui", "run", "bash"]);
941 let Commands::Run {
942 command,
943 args,
944 cwd,
945 cols,
946 rows,
947 } = cli.command
948 else {
949 panic!("Expected Run command, got {:?}", cli.command);
950 };
951 assert_eq!(command, "bash");
952 assert!(args.is_empty());
953 assert!(cwd.is_none());
954
955 assert_eq!(cols, 120, "Default cols should be 120");
956 assert_eq!(rows, 40, "Default rows should be 40");
957 }
958
959 #[test]
961 fn test_run_custom_dimensions() {
962 let cli = Cli::parse_from(["agent-tui", "run", "--cols", "80", "--rows", "24", "vim"]);
963 let Commands::Run {
964 cols,
965 rows,
966 command,
967 ..
968 } = cli.command
969 else {
970 panic!("Expected Run command, got {:?}", cli.command);
971 };
972 assert_eq!(cols, 80);
973 assert_eq!(rows, 24);
974 assert_eq!(command, "vim");
975 }
976
977 #[test]
979 fn test_run_with_args() {
980 let cli = Cli::parse_from(["agent-tui", "run", "vim", "--", "file.txt", "-n"]);
981 let Commands::Run { command, args, .. } = cli.command else {
982 panic!("Expected Run command, got {:?}", cli.command);
983 };
984 assert_eq!(command, "vim");
985 assert_eq!(args, vec!["file.txt".to_string(), "-n".to_string()]);
986 }
987
988 #[test]
990 fn test_snap_flags() {
991 let cli = Cli::parse_from(["agent-tui", "snap", "-i"]);
992 let Commands::Snap {
993 elements,
994 region,
995 strip_ansi,
996 include_cursor,
997 ..
998 } = cli.command
999 else {
1000 panic!("Expected Snap command, got {:?}", cli.command);
1001 };
1002 assert!(elements, "-i should enable elements");
1003 assert!(region.is_none());
1004 assert!(!strip_ansi);
1005 assert!(!include_cursor);
1006 }
1007
1008 #[test]
1010 fn test_snap_all_flags() {
1011 let cli = Cli::parse_from([
1012 "agent-tui",
1013 "snap",
1014 "-i",
1015 "--region",
1016 "modal",
1017 "--strip-ansi",
1018 "--include-cursor",
1019 ]);
1020 let Commands::Snap {
1021 elements,
1022 region,
1023 strip_ansi,
1024 include_cursor,
1025 ..
1026 } = cli.command
1027 else {
1028 panic!("Expected Snap command, got {:?}", cli.command);
1029 };
1030 assert!(elements);
1031 assert_eq!(region, Some("modal".to_string()));
1032 assert!(strip_ansi);
1033 assert!(include_cursor);
1034 }
1035
1036 #[test]
1038 fn test_snap_accessibility_flag() {
1039 let cli = Cli::parse_from(["agent-tui", "snap", "-a"]);
1040 let Commands::Snap {
1041 accessibility,
1042 elements,
1043 ..
1044 } = cli.command
1045 else {
1046 panic!("Expected Snap command, got {:?}", cli.command);
1047 };
1048 assert!(accessibility, "-a should enable accessibility tree format");
1049 assert!(!elements, "elements should be false by default");
1050 }
1051
1052 #[test]
1054 fn test_snap_accessibility_interactive_only() {
1055 let cli = Cli::parse_from(["agent-tui", "snap", "-a", "--interactive-only"]);
1056 let Commands::Snap {
1057 accessibility,
1058 interactive_only,
1059 ..
1060 } = cli.command
1061 else {
1062 panic!("Expected Snap command, got {:?}", cli.command);
1063 };
1064 assert!(accessibility, "--accessibility should be set");
1065 assert!(
1066 interactive_only,
1067 "--interactive-only should filter to interactive elements"
1068 );
1069 }
1070
1071 #[test]
1073 fn test_click_command() {
1074 let cli = Cli::parse_from(["agent-tui", "click", "@btn1"]);
1075 let Commands::Click {
1076 element_ref,
1077 double,
1078 } = cli.command
1079 else {
1080 panic!("Expected Click command, got {:?}", cli.command);
1081 };
1082 assert_eq!(element_ref, "@btn1");
1083 assert!(!double);
1084 }
1085
1086 #[test]
1088 fn test_click_double_flag() {
1089 let cli = Cli::parse_from(["agent-tui", "click", "@btn1", "--double"]);
1090 let Commands::Click {
1091 element_ref,
1092 double,
1093 } = cli.command
1094 else {
1095 panic!("Expected Click command, got {:?}", cli.command);
1096 };
1097 assert_eq!(element_ref, "@btn1");
1098 assert!(double);
1099
1100 let cli = Cli::parse_from(["agent-tui", "click", "-2", "@btn1"]);
1102 let Commands::Click { double, .. } = cli.command else {
1103 panic!("Expected Click command, got {:?}", cli.command);
1104 };
1105 assert!(double);
1106 }
1107
1108 #[test]
1110 fn test_fill_command() {
1111 let cli = Cli::parse_from(["agent-tui", "fill", "@inp1", "test value"]);
1112 let Commands::Fill { element_ref, value } = cli.command else {
1113 panic!("Expected Fill command, got {:?}", cli.command);
1114 };
1115 assert_eq!(element_ref, "@inp1");
1116 assert_eq!(value, "test value");
1117 }
1118
1119 #[test]
1121 fn test_key_command() {
1122 let test_cases = vec![
1123 "Enter",
1124 "Tab",
1125 "Escape",
1126 "Backspace",
1127 "Delete",
1128 "ArrowUp",
1129 "ArrowDown",
1130 "ArrowLeft",
1131 "ArrowRight",
1132 "Home",
1133 "End",
1134 "PageUp",
1135 "PageDown",
1136 "F1",
1137 "F10",
1138 "F12",
1139 "Ctrl+C",
1140 "Alt+F4",
1141 "Shift+Tab",
1142 ];
1143
1144 for k in test_cases {
1145 let cli = Cli::parse_from(["agent-tui", "key", k]);
1146 let Commands::Key {
1147 key,
1148 text,
1149 hold,
1150 release,
1151 } = cli.command
1152 else {
1153 panic!("Expected Key command for key: {k}, got {:?}", cli.command);
1154 };
1155 assert_eq!(key, Some(k.to_string()));
1156 assert!(text.is_none());
1157 assert!(!hold);
1158 assert!(!release);
1159 }
1160 }
1161
1162 #[test]
1164 fn test_key_type_command() {
1165 let cli = Cli::parse_from(["agent-tui", "key", "--type", "Hello, World!"]);
1166 let Commands::Key { key, text, .. } = cli.command else {
1167 panic!("Expected Key command, got {:?}", cli.command);
1168 };
1169 assert!(key.is_none());
1170 assert_eq!(text, Some("Hello, World!".to_string()));
1171
1172 let cli = Cli::parse_from(["agent-tui", "key", "-t", "hello"]);
1174 let Commands::Key { key, text, .. } = cli.command else {
1175 panic!("Expected Key command, got {:?}", cli.command);
1176 };
1177 assert!(key.is_none());
1178 assert_eq!(text, Some("hello".to_string()));
1179 }
1180
1181 #[test]
1183 fn test_key_hold_command() {
1184 let cli = Cli::parse_from(["agent-tui", "key", "Shift", "--hold"]);
1185 let Commands::Key {
1186 key, hold, release, ..
1187 } = cli.command
1188 else {
1189 panic!("Expected Key command, got {:?}", cli.command);
1190 };
1191 assert_eq!(key, Some("Shift".to_string()));
1192 assert!(hold);
1193 assert!(!release);
1194 }
1195
1196 #[test]
1198 fn test_key_release_command() {
1199 let cli = Cli::parse_from(["agent-tui", "key", "Shift", "--release"]);
1200 let Commands::Key {
1201 key, hold, release, ..
1202 } = cli.command
1203 else {
1204 panic!("Expected Key command, got {:?}", cli.command);
1205 };
1206 assert_eq!(key, Some("Shift".to_string()));
1207 assert!(!hold);
1208 assert!(release);
1209 }
1210
1211 #[test]
1213 fn test_key_flag_conflicts() {
1214 assert!(Cli::try_parse_from(["agent-tui", "key", "Shift", "--hold", "--release"]).is_err());
1216
1217 assert!(Cli::try_parse_from(["agent-tui", "key", "--type", "hello", "--hold"]).is_err());
1219
1220 assert!(Cli::try_parse_from(["agent-tui", "key", "--type", "hello", "--release"]).is_err());
1222 }
1223
1224 #[test]
1226 fn test_wait_defaults() {
1227 let cli = Cli::parse_from(["agent-tui", "wait", "Loading"]);
1228 let Commands::Wait { params } = cli.command else {
1229 panic!("Expected Wait command, got {:?}", cli.command);
1230 };
1231 assert_eq!(params.text, Some("Loading".to_string()));
1232
1233 assert_eq!(params.timeout, 30000, "Default timeout should be 30000ms");
1234 }
1235
1236 #[test]
1238 fn test_wait_custom_timeout() {
1239 let cli = Cli::parse_from(["agent-tui", "wait", "-t", "5000", "Done"]);
1240 let Commands::Wait { params } = cli.command else {
1241 panic!("Expected Wait command, got {:?}", cli.command);
1242 };
1243 assert_eq!(params.text, Some("Done".to_string()));
1244 assert_eq!(params.timeout, 5000);
1245 }
1246
1247 #[test]
1249 fn test_wait_stable() {
1250 let cli = Cli::parse_from(["agent-tui", "wait", "--stable"]);
1251 let Commands::Wait { params } = cli.command else {
1252 panic!("Expected Wait command, got {:?}", cli.command);
1253 };
1254 assert!(params.stable);
1255 assert!(params.text.is_none());
1256 }
1257
1258 #[test]
1260 fn test_wait_element() {
1261 let cli = Cli::parse_from(["agent-tui", "wait", "--element", "@btn1"]);
1262 let Commands::Wait { params } = cli.command else {
1263 panic!("Expected Wait command, got {:?}", cli.command);
1264 };
1265 assert_eq!(params.element, Some("@btn1".to_string()));
1266 assert!(params.text.is_none());
1267 }
1268
1269 #[test]
1271 fn test_wait_element_short_flag() {
1272 let cli = Cli::parse_from(["agent-tui", "wait", "-e", "@btn1"]);
1273 let Commands::Wait { params } = cli.command else {
1274 panic!("Expected Wait command, got {:?}", cli.command);
1275 };
1276 assert_eq!(params.element, Some("@btn1".to_string()));
1277 assert!(!params.gone);
1278 }
1279
1280 #[test]
1282 fn test_wait_focused() {
1283 let cli = Cli::parse_from(["agent-tui", "wait", "--focused", "@inp1"]);
1284 let Commands::Wait { params } = cli.command else {
1285 panic!("Expected Wait command, got {:?}", cli.command);
1286 };
1287 assert_eq!(params.focused, Some("@inp1".to_string()));
1288 assert!(params.text.is_none());
1289 }
1290
1291 #[test]
1293 fn test_wait_element_gone() {
1294 let cli = Cli::parse_from(["agent-tui", "wait", "-e", "@spinner", "--gone"]);
1295 let Commands::Wait { params } = cli.command else {
1296 panic!("Expected Wait command, got {:?}", cli.command);
1297 };
1298 assert_eq!(params.element, Some("@spinner".to_string()));
1299 assert!(params.gone);
1300 }
1301
1302 #[test]
1304 fn test_wait_text_gone() {
1305 let cli = Cli::parse_from(["agent-tui", "wait", "Loading...", "--gone"]);
1306 let Commands::Wait { params } = cli.command else {
1307 panic!("Expected Wait command, got {:?}", cli.command);
1308 };
1309 assert_eq!(params.text, Some("Loading...".to_string()));
1310 assert!(params.gone);
1311 }
1312
1313 #[test]
1315 fn test_wait_gone_short_flag() {
1316 let cli = Cli::parse_from(["agent-tui", "wait", "-e", "@spinner", "-g"]);
1317 let Commands::Wait { params } = cli.command else {
1318 panic!("Expected Wait command, got {:?}", cli.command);
1319 };
1320 assert_eq!(params.element, Some("@spinner".to_string()));
1321 assert!(params.gone);
1322 }
1323
1324 #[test]
1326 fn test_wait_value() {
1327 let cli = Cli::parse_from(["agent-tui", "wait", "--value", "@inp1=hello"]);
1328 let Commands::Wait { params } = cli.command else {
1329 panic!("Expected Wait command, got {:?}", cli.command);
1330 };
1331 assert_eq!(params.value, Some("@inp1=hello".to_string()));
1332 }
1333
1334 #[test]
1336 fn test_scroll_directions() {
1337 for (arg, expected) in [
1338 ("up", ScrollDirection::Up),
1339 ("down", ScrollDirection::Down),
1340 ("left", ScrollDirection::Left),
1341 ("right", ScrollDirection::Right),
1342 ] {
1343 let cli = Cli::parse_from(["agent-tui", "scroll", arg]);
1344 let Commands::Scroll {
1345 direction,
1346 amount,
1347 element,
1348 to_ref,
1349 } = cli.command
1350 else {
1351 panic!("Expected Scroll command for {arg}, got {:?}", cli.command);
1352 };
1353 assert_eq!(direction.unwrap() as u8, expected as u8);
1354
1355 assert_eq!(amount, 5, "Default scroll amount should be 5");
1356 assert!(element.is_none());
1357 assert!(to_ref.is_none());
1358 }
1359 }
1360
1361 #[test]
1363 fn test_scroll_to_flag() {
1364 let cli = Cli::parse_from(["agent-tui", "scroll", "--to", "@e5"]);
1365 let Commands::Scroll {
1366 direction, to_ref, ..
1367 } = cli.command
1368 else {
1369 panic!("Expected Scroll command, got {:?}", cli.command);
1370 };
1371 assert!(direction.is_none());
1372 assert_eq!(to_ref, Some("@e5".to_string()));
1373 }
1374
1375 #[test]
1377 fn test_scroll_custom_amount() {
1378 let cli = Cli::parse_from(["agent-tui", "scroll", "down", "-a", "10"]);
1379 let Commands::Scroll { amount, .. } = cli.command else {
1380 panic!("Expected Scroll command, got {:?}", cli.command);
1381 };
1382 assert_eq!(amount, 10);
1383 }
1384
1385 #[test]
1387 fn test_resize_defaults() {
1388 let cli = Cli::parse_from(["agent-tui", "resize"]);
1389 let Commands::Resize { cols, rows } = cli.command else {
1390 panic!("Expected Resize command, got {:?}", cli.command);
1391 };
1392
1393 assert_eq!(cols, 120, "Default cols should be 120");
1394 assert_eq!(rows, 40, "Default rows should be 40");
1395 }
1396
1397 #[test]
1399 fn test_trace_defaults() {
1400 let cli = Cli::parse_from(["agent-tui", "trace"]);
1401 let Commands::Trace { count, start, stop } = cli.command else {
1402 panic!("Expected Trace command, got {:?}", cli.command);
1403 };
1404
1405 assert_eq!(count, 10, "Default trace count should be 10");
1406 assert!(!start);
1407 assert!(!stop);
1408 }
1409
1410 #[test]
1412 fn test_console_defaults() {
1413 let cli = Cli::parse_from(["agent-tui", "console"]);
1414 let Commands::Console { lines, clear } = cli.command else {
1415 panic!("Expected Console command, got {:?}", cli.command);
1416 };
1417
1418 assert_eq!(lines, 100, "Default console lines should be 100");
1419 assert!(!clear, "Default clear should be false");
1420 }
1421
1422 #[test]
1424 fn test_assert_command() {
1425 let test_cases = vec!["text:Success", "element:@btn1", "session:main"];
1426
1427 for condition in test_cases {
1428 let cli = Cli::parse_from(["agent-tui", "assert", condition]);
1429 let Commands::Assert { condition: parsed } = cli.command else {
1430 panic!(
1431 "Expected Assert command for {condition}, got {:?}",
1432 cli.command
1433 );
1434 };
1435 assert_eq!(parsed, condition);
1436 }
1437 }
1438
1439 #[test]
1441 fn test_find_command() {
1442 let cli = Cli::parse_from(["agent-tui", "find", "--role", "button"]);
1443 let Commands::Find { params } = cli.command else {
1444 panic!("Expected Find command, got {:?}", cli.command);
1445 };
1446 assert_eq!(params.role, Some("button".to_string()));
1447 assert!(params.name.is_none());
1448 assert!(params.text.is_none());
1449 assert!(params.placeholder.is_none());
1450 assert!(!params.focused);
1451 assert!(params.nth.is_none());
1452 assert!(!params.exact);
1453
1454 let cli = Cli::parse_from(["agent-tui", "find", "--role", "button", "--name", "Submit"]);
1455 let Commands::Find { params } = cli.command else {
1456 panic!("Expected Find command, got {:?}", cli.command);
1457 };
1458 assert_eq!(params.role, Some("button".to_string()));
1459 assert_eq!(params.name, Some("Submit".to_string()));
1460
1461 let cli = Cli::parse_from(["agent-tui", "find", "--focused"]);
1462 let Commands::Find { params } = cli.command else {
1463 panic!("Expected Find command, got {:?}", cli.command);
1464 };
1465 assert!(params.focused);
1466
1467 let cli = Cli::parse_from(["agent-tui", "find", "--role", "button", "--nth", "2"]);
1468 let Commands::Find { params } = cli.command else {
1469 panic!("Expected Find command, got {:?}", cli.command);
1470 };
1471 assert_eq!(params.nth, Some(2));
1472
1473 let cli = Cli::parse_from(["agent-tui", "find", "--text", "Submit", "--exact"]);
1474 let Commands::Find { params } = cli.command else {
1475 panic!("Expected Find command, got {:?}", cli.command);
1476 };
1477 assert_eq!(params.text, Some("Submit".to_string()));
1478 assert!(params.exact);
1479
1480 let cli = Cli::parse_from(["agent-tui", "find", "--placeholder", "Search..."]);
1481 let Commands::Find { params } = cli.command else {
1482 panic!("Expected Find command, got {:?}", cli.command);
1483 };
1484 assert_eq!(params.placeholder, Some("Search...".to_string()));
1485 }
1486
1487 #[test]
1489 fn test_missing_required_args() {
1490 assert!(Cli::try_parse_from(["agent-tui", "click"]).is_err());
1491
1492 assert!(Cli::try_parse_from(["agent-tui", "fill"]).is_err());
1493 assert!(Cli::try_parse_from(["agent-tui", "fill", "@inp1"]).is_err());
1494
1495 assert!(Cli::try_parse_from(["agent-tui", "run"]).is_err());
1496
1497 assert!(Cli::try_parse_from(["agent-tui", "scroll"]).is_err());
1498 }
1499
1500 #[test]
1502 fn test_output_format_values() {
1503 let cli = Cli::parse_from(["agent-tui", "-f", "text", "status"]);
1504 assert_eq!(cli.format, OutputFormat::Text);
1505
1506 let cli = Cli::parse_from(["agent-tui", "-f", "json", "status"]);
1507 assert_eq!(cli.format, OutputFormat::Json);
1508
1509 assert!(Cli::try_parse_from(["agent-tui", "-f", "xml", "status"]).is_err());
1510 }
1511
1512 #[test]
1514 fn test_json_shorthand_flag() {
1515 let cli = Cli::parse_from(["agent-tui", "--json", "status"]);
1516 assert!(cli.json);
1517 }
1518
1519 #[test]
1521 fn test_run_with_cwd() {
1522 let cli = Cli::parse_from(["agent-tui", "run", "-d", "/tmp", "bash"]);
1523 let Commands::Run { command, cwd, .. } = cli.command else {
1524 panic!("Expected Run command, got {:?}", cli.command);
1525 };
1526 assert_eq!(command, "bash");
1527 assert_eq!(cwd, Some("/tmp".to_string()));
1528 }
1529
1530 #[test]
1532 fn test_toggle_command() {
1533 let cli = Cli::parse_from(["agent-tui", "toggle", "@cb1"]);
1534 let Commands::Toggle {
1535 element_ref,
1536 on,
1537 off,
1538 } = cli.command
1539 else {
1540 panic!("Expected Toggle command, got {:?}", cli.command);
1541 };
1542 assert_eq!(element_ref, "@cb1");
1543 assert!(!on);
1544 assert!(!off);
1545 }
1546
1547 #[test]
1549 fn test_toggle_with_on_flag() {
1550 let cli = Cli::parse_from(["agent-tui", "toggle", "@cb1", "--on"]);
1551 let Commands::Toggle {
1552 element_ref,
1553 on,
1554 off,
1555 } = cli.command
1556 else {
1557 panic!("Expected Toggle command, got {:?}", cli.command);
1558 };
1559 assert_eq!(element_ref, "@cb1");
1560 assert!(on);
1561 assert!(!off);
1562 }
1563
1564 #[test]
1566 fn test_toggle_with_off_flag() {
1567 let cli = Cli::parse_from(["agent-tui", "toggle", "@cb1", "--off"]);
1568 let Commands::Toggle {
1569 element_ref,
1570 on,
1571 off,
1572 } = cli.command
1573 else {
1574 panic!("Expected Toggle command, got {:?}", cli.command);
1575 };
1576 assert_eq!(element_ref, "@cb1");
1577 assert!(!on);
1578 assert!(off);
1579 }
1580
1581 #[test]
1583 fn test_toggle_on_off_mutually_exclusive() {
1584 let result = Cli::try_parse_from(["agent-tui", "toggle", "@cb1", "--on", "--off"]);
1585 assert!(result.is_err());
1586 }
1587
1588 #[test]
1590 fn test_errors_command_with_count() {
1591 let cli = Cli::parse_from(["agent-tui", "errors", "-n", "25"]);
1592 let Commands::Errors { count, clear } = cli.command else {
1593 panic!("Expected Errors command, got {:?}", cli.command);
1594 };
1595 assert_eq!(count, 25);
1596 assert!(!clear);
1597 }
1598
1599 #[test]
1601 fn test_errors_command_with_clear() {
1602 let cli = Cli::parse_from(["agent-tui", "errors", "--clear"]);
1603 let Commands::Errors { clear, .. } = cli.command else {
1604 panic!("Expected Errors command, got {:?}", cli.command);
1605 };
1606 assert!(clear);
1607 }
1608
1609 #[test]
1611 fn test_attach_command() {
1612 let cli = Cli::parse_from(["agent-tui", "attach", "my-session"]);
1613 let Commands::Attach {
1614 session_id,
1615 interactive,
1616 } = cli.command
1617 else {
1618 panic!("Expected Attach command, got {:?}", cli.command);
1619 };
1620 assert_eq!(session_id, "my-session");
1621 assert!(!interactive);
1622 }
1623
1624 #[test]
1626 fn test_attach_command_interactive() {
1627 let cli = Cli::parse_from(["agent-tui", "attach", "-i", "my-session"]);
1628 let Commands::Attach {
1629 session_id,
1630 interactive,
1631 } = cli.command
1632 else {
1633 panic!("Expected Attach command, got {:?}", cli.command);
1634 };
1635 assert_eq!(session_id, "my-session");
1636 assert!(interactive);
1637 }
1638
1639 #[test]
1641 fn test_record_start_command() {
1642 let cli = Cli::parse_from(["agent-tui", "record-start"]);
1643 assert!(matches!(cli.command, Commands::RecordStart));
1644 }
1645
1646 #[test]
1648 fn test_record_stop_command() {
1649 let cli = Cli::parse_from(["agent-tui", "record-stop"]);
1650 let Commands::RecordStop {
1651 output,
1652 record_format,
1653 } = cli.command
1654 else {
1655 panic!("Expected RecordStop command, got {:?}", cli.command);
1656 };
1657 assert!(output.is_none());
1658 assert!(matches!(record_format, RecordFormat::Json));
1659 }
1660
1661 #[test]
1663 fn test_record_stop_with_output() {
1664 let cli = Cli::parse_from(["agent-tui", "record-stop", "-o", "recording.json"]);
1665 let Commands::RecordStop { output, .. } = cli.command else {
1666 panic!("Expected RecordStop command, got {:?}", cli.command);
1667 };
1668 assert_eq!(output, Some("recording.json".to_string()));
1669 }
1670
1671 #[test]
1673 fn test_record_stop_asciicast_format() {
1674 let cli = Cli::parse_from(["agent-tui", "record-stop", "--record-format", "asciicast"]);
1675 let Commands::RecordStop { record_format, .. } = cli.command else {
1676 panic!("Expected RecordStop command, got {:?}", cli.command);
1677 };
1678 assert!(matches!(record_format, RecordFormat::Asciicast));
1679 }
1680
1681 #[test]
1683 fn test_record_status_command() {
1684 let cli = Cli::parse_from(["agent-tui", "record-status"]);
1685 assert!(matches!(cli.command, Commands::RecordStatus));
1686 }
1687
1688 #[test]
1691 fn test_restart_command() {
1692 let cli = Cli::parse_from(["agent-tui", "restart"]);
1693 assert!(matches!(cli.command, Commands::Restart));
1694 }
1695
1696 #[test]
1698 fn test_selectall_command() {
1699 let cli = Cli::parse_from(["agent-tui", "selectall", "@inp1"]);
1700 let Commands::SelectAll { element_ref } = cli.command else {
1701 panic!("Expected SelectAll command, got {:?}", cli.command);
1702 };
1703 assert_eq!(element_ref, "@inp1");
1704 }
1705
1706 #[test]
1708 fn test_multiselect_command() {
1709 let cli = Cli::parse_from(["agent-tui", "multiselect", "@sel1", "opt1", "opt2", "opt3"]);
1710 let Commands::MultiSelect {
1711 element_ref,
1712 options,
1713 } = cli.command
1714 else {
1715 panic!("Expected MultiSelect command, got {:?}", cli.command);
1716 };
1717 assert_eq!(element_ref, "@sel1");
1718 assert_eq!(options, vec!["opt1", "opt2", "opt3"]);
1719 }
1720
1721 #[test]
1723 fn test_cleanup_command() {
1724 let cli = Cli::parse_from(["agent-tui", "cleanup"]);
1725 let Commands::Cleanup { all } = cli.command else {
1726 panic!("Expected Cleanup command, got {:?}", cli.command);
1727 };
1728 assert!(!all);
1729 }
1730
1731 #[test]
1733 fn test_cleanup_command_all() {
1734 let cli = Cli::parse_from(["agent-tui", "cleanup", "--all"]);
1735 let Commands::Cleanup { all } = cli.command else {
1736 panic!("Expected Cleanup command, got {:?}", cli.command);
1737 };
1738 assert!(all);
1739 }
1740
1741 #[test]
1743 fn test_select_command() {
1744 let cli = Cli::parse_from(["agent-tui", "select", "@sel1", "option1"]);
1745 let Commands::Select {
1746 element_ref,
1747 option,
1748 } = cli.command
1749 else {
1750 panic!("Expected Select command, got {:?}", cli.command);
1751 };
1752 assert_eq!(element_ref, "@sel1");
1753 assert_eq!(option, "option1");
1754 }
1755
1756 #[test]
1758 fn test_scroll_with_element() {
1759 let cli = Cli::parse_from(["agent-tui", "scroll", "down", "-e", "@list1"]);
1760 let Commands::Scroll { element, .. } = cli.command else {
1761 panic!("Expected Scroll command, got {:?}", cli.command);
1762 };
1763 assert_eq!(element, Some("@list1".to_string()));
1764 }
1765
1766 #[test]
1768 fn test_count_command_role_and_name() {
1769 let cli = Cli::parse_from(["agent-tui", "count", "--role", "button", "--name", "Submit"]);
1770 let Commands::Count { role, name, text } = cli.command else {
1771 panic!("Expected Count command, got {:?}", cli.command);
1772 };
1773 assert_eq!(role, Some("button".to_string()));
1774 assert_eq!(name, Some("Submit".to_string()));
1775 assert!(text.is_none());
1776 }
1777
1778 #[test]
1780 fn test_version_command() {
1781 let cli = Cli::parse_from(["agent-tui", "version"]);
1782 assert!(matches!(cli.command, Commands::Version));
1783 }
1784
1785 #[test]
1787 fn test_env_command() {
1788 let cli = Cli::parse_from(["agent-tui", "env"]);
1789 assert!(matches!(cli.command, Commands::Env));
1790 }
1791
1792 #[test]
1794 fn test_ls_command() {
1795 let cli = Cli::parse_from(["agent-tui", "ls"]);
1796 assert!(matches!(cli.command, Commands::Ls));
1797 }
1798
1799 #[test]
1801 fn test_kill_command() {
1802 let cli = Cli::parse_from(["agent-tui", "kill"]);
1803 assert!(matches!(cli.command, Commands::Kill));
1804 }
1805
1806 #[test]
1808 fn test_status_verbose() {
1809 let cli = Cli::parse_from(["agent-tui", "status", "-v"]);
1810 let Commands::Status { verbose } = cli.command else {
1811 panic!("Expected Status command, got {:?}", cli.command);
1812 };
1813 assert!(verbose);
1814 }
1815
1816 #[test]
1818 fn test_completions_command() {
1819 let cli = Cli::parse_from(["agent-tui", "completions", "bash"]);
1820 let Commands::Completions { shell } = cli.command else {
1821 panic!("Expected Completions command, got {:?}", cli.command);
1822 };
1823 assert!(matches!(shell, Shell::Bash));
1824 }
1825
1826 #[test]
1828 fn test_completions_fish() {
1829 let cli = Cli::parse_from(["agent-tui", "completions", "fish"]);
1830 let Commands::Completions { shell } = cli.command else {
1831 panic!("Expected Completions command, got {:?}", cli.command);
1832 };
1833 assert!(matches!(shell, Shell::Fish));
1834 }
1835
1836 #[test]
1838 fn test_daemon_start_default() {
1839 let cli = Cli::parse_from(["agent-tui", "daemon", "start"]);
1840 let Commands::Daemon(DaemonCommand::Start { foreground }) = cli.command else {
1841 panic!("Expected Daemon Start command, got {:?}", cli.command);
1842 };
1843 assert!(!foreground, "Default should be background mode");
1844 }
1845
1846 #[test]
1848 fn test_daemon_start_foreground() {
1849 let cli = Cli::parse_from(["agent-tui", "daemon", "start", "--foreground"]);
1850 let Commands::Daemon(DaemonCommand::Start { foreground }) = cli.command else {
1851 panic!("Expected Daemon Start command, got {:?}", cli.command);
1852 };
1853 assert!(foreground, "Should be foreground mode");
1854 }
1855
1856 #[test]
1858 fn test_daemon_stop_default() {
1859 let cli = Cli::parse_from(["agent-tui", "daemon", "stop"]);
1860 let Commands::Daemon(DaemonCommand::Stop { force }) = cli.command else {
1861 panic!("Expected Daemon Stop command, got {:?}", cli.command);
1862 };
1863 assert!(!force, "Default should be graceful stop");
1864 }
1865
1866 #[test]
1868 fn test_daemon_stop_force() {
1869 let cli = Cli::parse_from(["agent-tui", "daemon", "stop", "--force"]);
1870 let Commands::Daemon(DaemonCommand::Stop { force }) = cli.command else {
1871 panic!("Expected Daemon Stop command, got {:?}", cli.command);
1872 };
1873 assert!(force, "Should be force stop");
1874 }
1875
1876 #[test]
1878 fn test_daemon_status() {
1879 let cli = Cli::parse_from(["agent-tui", "daemon", "status"]);
1880 assert!(matches!(
1881 cli.command,
1882 Commands::Daemon(DaemonCommand::Status)
1883 ));
1884 }
1885
1886 #[test]
1888 fn test_daemon_restart() {
1889 let cli = Cli::parse_from(["agent-tui", "daemon", "restart"]);
1890 assert!(matches!(
1891 cli.command,
1892 Commands::Daemon(DaemonCommand::Restart)
1893 ));
1894 }
1895}