aboutsummaryrefslogtreecommitdiff
path: root/src/cli/pane.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-21 20:19:54 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-21 20:22:57 +0300
commit866aaf740e10fcfce169aaf81380ea0257fbe272 (patch)
treef815392b7c6b24a7c0de14e655bd635656383813 /src/cli/pane.rs
parentca579c402d5668560ca13059f82c03178be18b2a (diff)
refactor: separate server client boundaries
Diffstat (limited to 'src/cli/pane.rs')
-rw-r--r--src/cli/pane.rs410
1 files changed, 410 insertions, 0 deletions
diff --git a/src/cli/pane.rs b/src/cli/pane.rs
new file mode 100644
index 00000000..58d65af6
--- /dev/null
+++ b/src/cli/pane.rs
@@ -0,0 +1,410 @@
+use crate::api::schema::{
+ Method, PaneListParams, PaneReadParams, PaneRenameParams, PaneReportAgentParams,
+ PaneSendInputParams, PaneSendKeysParams, PaneSendTextParams, PaneSplitParams, PaneTarget,
+ ReadFormat, ReadSource, Request,
+};
+
+pub(super) fn run_pane_command(args: &[String]) -> std::io::Result<i32> {
+ 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..]),
+ "rename" => pane_rename(&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..]),
+ "report-agent" => pane_report_agent(&args[1..]),
+ "run" => pane_run(&args[1..]),
+ "help" | "--help" | "-h" => {
+ print_pane_help();
+ Ok(0)
+ }
+ _ => {
+ print_pane_help();
+ Ok(2)
+ }
+ }
+}
+
+fn pane_list(args: &[String]) -> std::io::Result<i32> {
+ let mut workspace_id = None;
+
+ let mut index = 0;
+ while index < args.len() {
+ match args[index].as_str() {
+ "--workspace" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --workspace");
+ return Ok(2);
+ };
+ workspace_id = Some(super::normalize_workspace_id(value));
+ index += 2;
+ }
+ other => {
+ eprintln!("unknown option: {other}");
+ return Ok(2);
+ }
+ }
+ }
+
+ super::print_response(&super::send_request(&Request {
+ id: "cli:pane:list".into(),
+ method: Method::PaneList(PaneListParams { workspace_id }),
+ })?)
+}
+
+fn pane_get(args: &[String]) -> std::io::Result<i32> {
+ let Some(raw_pane_id) = args.first() else {
+ eprintln!("usage: herdr pane get <pane_id>");
+ return Ok(2);
+ };
+ if args.len() != 1 {
+ eprintln!("usage: herdr pane get <pane_id>");
+ return Ok(2);
+ }
+
+ super::print_response(&super::send_request(&Request {
+ id: "cli:pane:get".into(),
+ method: Method::PaneGet(PaneTarget {
+ pane_id: super::normalize_pane_id(raw_pane_id),
+ }),
+ })?)
+}
+
+fn pane_rename(args: &[String]) -> std::io::Result<i32> {
+ let Some(raw_pane_id) = args.first() else {
+ eprintln!("usage: herdr pane rename <pane_id> <label>|--clear");
+ return Ok(2);
+ };
+ if args.len() < 2 {
+ eprintln!("usage: herdr pane rename <pane_id> <label>|--clear");
+ return Ok(2);
+ }
+ let label = if args.len() == 2 && args[1] == "--clear" {
+ None
+ } else {
+ Some(args[1..].join(" "))
+ };
+
+ super::print_response(&super::send_request(&Request {
+ id: "cli:pane:rename".into(),
+ method: Method::PaneRename(PaneRenameParams {
+ pane_id: super::normalize_pane_id(raw_pane_id),
+ label,
+ }),
+ })?)
+}
+
+fn pane_read(args: &[String]) -> std::io::Result<i32> {
+ let Some(raw_pane_id) = args.first() else {
+ eprintln!("usage: herdr pane read <pane_id> [--source visible|recent|recent-unwrapped] [--lines N] [--format text|ansi] [--ansi]");
+ return Ok(2);
+ };
+
+ let pane_id = super::normalize_pane_id(raw_pane_id);
+ let mut source = ReadSource::Recent;
+ let mut lines = None;
+ let mut format = ReadFormat::Text;
+ let mut strip_ansi = true;
+
+ let mut index = 1;
+ while index < args.len() {
+ match args[index].as_str() {
+ "--source" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --source");
+ return Ok(2);
+ };
+ source = super::parse_read_source(value)?;
+ index += 2;
+ }
+ "--lines" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --lines");
+ return Ok(2);
+ };
+ lines = Some(super::parse_u32_flag("--lines", value)?);
+ index += 2;
+ }
+ "--format" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --format");
+ return Ok(2);
+ };
+ format = super::parse_read_format(value)?;
+ index += 2;
+ }
+ "--ansi" => {
+ format = ReadFormat::Ansi;
+ index += 1;
+ }
+ "--raw" => {
+ format = ReadFormat::Ansi;
+ strip_ansi = false;
+ index += 1;
+ }
+ other => {
+ eprintln!("unknown option: {other}");
+ return Ok(2);
+ }
+ }
+ }
+
+ let response = super::send_request(&Request {
+ id: "cli:pane:read".into(),
+ method: Method::PaneRead(PaneReadParams {
+ pane_id,
+ source,
+ lines,
+ format,
+ strip_ansi,
+ }),
+ })?;
+
+ if let Some(error) = response.get("error") {
+ eprintln!("{}", serde_json::to_string(error).unwrap());
+ return Ok(1);
+ }
+
+ if let Some(text) = response["result"]["read"]["text"].as_str() {
+ print!("{text}");
+ }
+ Ok(0)
+}
+
+fn pane_split(args: &[String]) -> std::io::Result<i32> {
+ let Some(raw_pane_id) = args.first() else {
+ eprintln!(
+ "usage: herdr pane split <pane_id> --direction right|down [--cwd PATH] [--focus] [--no-focus]"
+ );
+ return Ok(2);
+ };
+
+ let pane_id = super::normalize_pane_id(raw_pane_id);
+ let mut direction = None;
+ let mut cwd = None;
+ let mut focus = false;
+
+ let mut index = 1;
+ while index < args.len() {
+ match args[index].as_str() {
+ "--direction" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --direction");
+ return Ok(2);
+ };
+ direction = Some(super::parse_split_direction(value)?);
+ index += 2;
+ }
+ "--cwd" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --cwd");
+ return Ok(2);
+ };
+ cwd = Some(value.clone());
+ index += 2;
+ }
+ "--focus" => {
+ focus = true;
+ index += 1;
+ }
+ "--no-focus" => {
+ focus = false;
+ index += 1;
+ }
+ other => {
+ eprintln!("unknown option: {other}");
+ return Ok(2);
+ }
+ }
+ }
+
+ let Some(direction) = direction else {
+ eprintln!("missing required --direction");
+ return Ok(2);
+ };
+
+ super::print_response(&super::send_request(&Request {
+ id: "cli:pane:split".into(),
+ method: Method::PaneSplit(PaneSplitParams {
+ workspace_id: None,
+ target_pane_id: pane_id,
+ direction,
+ cwd,
+ focus,
+ }),
+ })?)
+}
+
+fn pane_close(args: &[String]) -> std::io::Result<i32> {
+ let Some(raw_pane_id) = args.first() else {
+ eprintln!("usage: herdr pane close <pane_id>");
+ return Ok(2);
+ };
+ if args.len() != 1 {
+ eprintln!("usage: herdr pane close <pane_id>");
+ return Ok(2);
+ }
+
+ super::print_response(&super::send_request(&Request {
+ id: "cli:pane:close".into(),
+ method: Method::PaneClose(PaneTarget {
+ pane_id: super::normalize_pane_id(raw_pane_id),
+ }),
+ })?)
+}
+
+fn pane_send_text(args: &[String]) -> std::io::Result<i32> {
+ if args.len() < 2 {
+ eprintln!("usage: herdr pane send-text <pane_id> <text>");
+ return Ok(2);
+ }
+
+ let pane_id = super::normalize_pane_id(&args[0]);
+ let text = args[1..].join(" ");
+ super::send_ok_request(Method::PaneSendText(PaneSendTextParams { pane_id, text }))
+}
+
+fn pane_send_keys(args: &[String]) -> std::io::Result<i32> {
+ if args.len() < 2 {
+ eprintln!("usage: herdr pane send-keys <pane_id> <key> [key ...]");
+ return Ok(2);
+ }
+
+ let pane_id = super::normalize_pane_id(&args[0]);
+ let keys = args[1..].to_vec();
+ super::send_ok_request(Method::PaneSendKeys(PaneSendKeysParams { pane_id, keys }))
+}
+
+fn pane_run(args: &[String]) -> std::io::Result<i32> {
+ if args.len() < 2 {
+ eprintln!("usage: herdr pane run <pane_id> <command>");
+ return Ok(2);
+ }
+
+ let pane_id = super::normalize_pane_id(&args[0]);
+ let text = args[1..].join(" ");
+ super::send_ok_request(Method::PaneSendInput(PaneSendInputParams {
+ pane_id,
+ text,
+ keys: vec!["Enter".into()],
+ }))
+}
+
+fn pane_report_agent(args: &[String]) -> std::io::Result<i32> {
+ let Some(raw_pane_id) = args.first() else {
+ eprintln!("usage: herdr pane report-agent <pane_id> --source ID --agent LABEL --state idle|working|blocked|unknown [--message TEXT] [--custom-status TEXT] [--seq N]");
+ return Ok(2);
+ };
+
+ let pane_id = super::normalize_pane_id(raw_pane_id);
+ let mut source = None;
+ let mut agent = None;
+ let mut state = None;
+ let mut message = None;
+ let mut custom_status = None;
+ let mut seq = None;
+
+ let mut index = 1;
+ while index < args.len() {
+ match args[index].as_str() {
+ "--source" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --source");
+ return Ok(2);
+ };
+ source = Some(value.clone());
+ index += 2;
+ }
+ "--agent" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --agent");
+ return Ok(2);
+ };
+ agent = Some(value.clone());
+ index += 2;
+ }
+ "--state" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --state");
+ return Ok(2);
+ };
+ state = Some(super::parse_pane_agent_state(value)?);
+ index += 2;
+ }
+ "--message" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --message");
+ return Ok(2);
+ };
+ message = Some(value.clone());
+ index += 2;
+ }
+ "--custom-status" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --custom-status");
+ return Ok(2);
+ };
+ custom_status = Some(value.clone());
+ index += 2;
+ }
+ "--seq" => {
+ let Some(value) = args.get(index + 1) else {
+ eprintln!("missing value for --seq");
+ return Ok(2);
+ };
+ seq = Some(super::parse_u64_flag("--seq", value)?);
+ index += 2;
+ }
+ other => {
+ eprintln!("unknown option: {other}");
+ return Ok(2);
+ }
+ }
+ }
+
+ let Some(source) = source else {
+ eprintln!("missing required --source");
+ return Ok(2);
+ };
+ let Some(agent) = agent else {
+ eprintln!("missing required --agent");
+ return Ok(2);
+ };
+ let Some(state) = state else {
+ eprintln!("missing required --state");
+ return Ok(2);
+ };
+
+ super::send_ok_request(Method::PaneReportAgent(PaneReportAgentParams {
+ pane_id,
+ source,
+ agent,
+ state,
+ message,
+ custom_status,
+ seq,
+ }))
+}
+
+fn print_pane_help() {
+ eprintln!("herdr pane commands:");
+ eprintln!(" herdr pane list [--workspace <workspace_id>]");
+ eprintln!(" herdr pane get <pane_id>");
+ eprintln!(" herdr pane rename <pane_id> <label>|--clear");
+ eprintln!(" herdr pane read <pane_id> [--source visible|recent|recent-unwrapped] [--lines N] [--format text|ansi] [--ansi]");
+ eprintln!(
+ " herdr pane split <pane_id> --direction right|down [--cwd PATH] [--focus] [--no-focus]"
+ );
+ eprintln!(" herdr pane close <pane_id>");
+ eprintln!(" herdr pane send-text <pane_id> <text>");
+ eprintln!(" herdr pane send-keys <pane_id> <key> [key ...]");
+ eprintln!(" herdr pane report-agent <pane_id> --source ID --agent LABEL --state idle|working|blocked|unknown [--message TEXT] [--custom-status TEXT] [--seq N]");
+ eprintln!(" herdr pane run <pane_id> <command>");
+}