From a5847a8039d2754dd2908a13e4625b31f7404d9a Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Sun, 29 Mar 2026 19:29:23 +0300 Subject: feat(cli): add workspace and pane management commands --- src/cli.rs | 762 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 14 +- 2 files changed, 775 insertions(+), 1 deletion(-) create mode 100644 src/cli.rs (limited to 'src') diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 00000000..724a8f57 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,762 @@ +use std::io::{BufRead, BufReader, Write}; +use std::os::unix::net::UnixStream; +use std::time::Duration; + +use serde::Serialize; + +use crate::api; +use crate::api::schema::{ + EmptyParams, Method, OutputMatch, PaneAgentState, PaneListParams, PaneReadParams, + PaneSendKeysParams, PaneSendTextParams, PaneSplitParams, PaneTarget, PaneWaitForOutputParams, + ReadSource, Request, SplitDirection, Subscription, WorkspaceCreateParams, + WorkspaceRenameParams, WorkspaceTarget, +}; + +pub enum CommandOutcome { + Handled(i32), + NotCli, +} + +pub fn maybe_run(args: &[String]) -> std::io::Result { + let Some(command) = args.get(1).map(|arg| arg.as_str()) else { + return Ok(CommandOutcome::NotCli); + }; + + let exit_code = match command { + "workspace" => run_workspace_command(&args[2..])?, + "pane" => run_pane_command(&args[2..])?, + "wait" => run_wait_command(&args[2..])?, + _ => return Ok(CommandOutcome::NotCli), + }; + + Ok(CommandOutcome::Handled(exit_code)) +} + +fn run_workspace_command(args: &[String]) -> std::io::Result { + let Some(subcommand) = args.first().map(|arg| arg.as_str()) else { + print_workspace_help(); + return Ok(2); + }; + + match subcommand { + "list" => workspace_list(&args[1..]), + "create" => workspace_create(&args[1..]), + "get" => workspace_get(&args[1..]), + "focus" => workspace_focus(&args[1..]), + "rename" => workspace_rename(&args[1..]), + "close" => workspace_close(&args[1..]), + _ => { + print_workspace_help(); + Ok(2) + } + } +} + +fn run_pane_command(args: &[String]) -> std::io::Result { + let Some(subcommand) = args.first().map(|arg| arg.as_str()) else { + print_pane_help(); + return Ok(2); + }; + + match subcommand { + "list" => pane_list(&args[1..]), + "get" => pane_get(&args[1..]), + "read" => pane_read(&args[1..]), + "split" => pane_split(&args[1..]), + "close" => pane_close(&args[1..]), + "send-text" => pane_send_text(&args[1..]), + "send-keys" => pane_send_keys(&args[1..]), + "run" => pane_run(&args[1..]), + _ => { + print_pane_help(); + Ok(2) + } + } +} + +fn run_wait_command(args: &[String]) -> std::io::Result { + let Some(subcommand) = args.first().map(|arg| arg.as_str()) else { + print_wait_help(); + return Ok(2); + }; + + match subcommand { + "output" => wait_output(&args[1..]), + "agent-state" => wait_agent_state(&args[1..]), + _ => { + print_wait_help(); + Ok(2) + } + } +} + +fn workspace_list(args: &[String]) -> std::io::Result { + if !args.is_empty() { + eprintln!("usage: herdr workspace list"); + return Ok(2); + } + + print_response(&send_request(&Request { + id: "cli:workspace:list".into(), + method: Method::WorkspaceList(EmptyParams::default()), + })?) +} + +fn workspace_create(args: &[String]) -> std::io::Result { + let mut cwd = None; + let mut focus = true; + + let mut index = 0; + while index < args.len() { + match args[index].as_str() { + "--cwd" => { + let Some(value) = args.get(index + 1) else { + eprintln!("missing value for --cwd"); + return Ok(2); + }; + cwd = Some(value.clone()); + index += 2; + } + "--no-focus" => { + focus = false; + index += 1; + } + other => { + eprintln!("unknown option: {other}"); + return Ok(2); + } + } + } + + print_response(&send_request(&Request { + id: "cli:workspace:create".into(), + method: Method::WorkspaceCreate(WorkspaceCreateParams { cwd, focus }), + })?) +} + +fn workspace_get(args: &[String]) -> std::io::Result { + let Some(raw_workspace_id) = args.first() else { + eprintln!("usage: herdr workspace get "); + return Ok(2); + }; + if args.len() != 1 { + eprintln!("usage: herdr workspace get "); + return Ok(2); + } + + print_response(&send_request(&Request { + id: "cli:workspace:get".into(), + method: Method::WorkspaceGet(WorkspaceTarget { + workspace_id: normalize_workspace_id(raw_workspace_id), + }), + })?) +} + +fn workspace_focus(args: &[String]) -> std::io::Result { + let Some(raw_workspace_id) = args.first() else { + eprintln!("usage: herdr workspace focus "); + return Ok(2); + }; + if args.len() != 1 { + eprintln!("usage: herdr workspace focus "); + return Ok(2); + } + + print_response(&send_request(&Request { + id: "cli:workspace:focus".into(), + method: Method::WorkspaceFocus(WorkspaceTarget { + workspace_id: normalize_workspace_id(raw_workspace_id), + }), + })?) +} + +fn workspace_rename(args: &[String]) -> std::io::Result { + if args.len() < 2 { + eprintln!("usage: herdr workspace rename