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::{ AgentStatus, EmptyParams, Method, OutputMatch, PaneListParams, PaneReadParams, PaneSendInputParams, PaneSendKeysParams, PaneSendTextParams, PaneSplitParams, PaneTarget, PaneWaitForOutputParams, ReadSource, Request, SplitDirection, Subscription, TabCreateParams, TabListParams, TabRenameParams, TabTarget, 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..])?, "tab" => run_tab_command(&args[2..])?, "pane" => run_pane_command(&args[2..])?, "wait" => run_wait_command(&args[2..])?, "integration" => run_integration_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_tab_command(args: &[String]) -> std::io::Result { let Some(subcommand) = args.first().map(|arg| arg.as_str()) else { print_tab_help(); return Ok(2); }; match subcommand { "list" => tab_list(&args[1..]), "create" => tab_create(&args[1..]), "get" => tab_get(&args[1..]), "focus" => tab_focus(&args[1..]), "rename" => tab_rename(&args[1..]), "close" => tab_close(&args[1..]), _ => { print_tab_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-status" => wait_agent_status(&args[1..]), _ => { print_wait_help(); Ok(2) } } } fn run_integration_command(args: &[String]) -> std::io::Result { let Some(subcommand) = args.first().map(|arg| arg.as_str()) else { print_integration_help(); return Ok(2); }; match subcommand { "install" => integration_install(&args[1..]), "uninstall" => integration_uninstall(&args[1..]), _ => { print_integration_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 label = None; 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; } "--label" => { let Some(value) = args.get(index + 1) else { eprintln!("missing value for --label"); return Ok(2); }; label = 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, label }), })?) } 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