// Tmux interaction: pane detection and prompt injection. use std::process::Command; use std::thread; use std::time::Duration; use tracing::info; /// Find Claude Code's tmux pane by scanning for the "claude" process. pub fn find_claude_pane() -> Option { let out = Command::new("tmux") .args([ "list-panes", "-a", "-F", "#{session_name}:#{window_index}.#{pane_index}\t#{pane_current_command}", ]) .output() .ok()?; let stdout = String::from_utf8_lossy(&out.stdout); for line in stdout.lines() { if let Some((pane, cmd)) = line.split_once('\t') { if cmd == "claude" { return Some(pane.to_string()); } } } None } /// Send a prompt to a tmux pane. Returns true on success. /// /// Types the message literally then presses Enter. pub fn send_prompt(pane: &str, msg: &str) -> bool { let preview: String = msg.chars().take(100).collect(); info!("SEND [{pane}]: {preview}..."); // Type the message literally (flatten newlines — they'd submit the input early) let flat: String = msg.chars().map(|c| if c == '\n' { ' ' } else { c }).collect(); let ok = Command::new("tmux") .args(["send-keys", "-t", pane, "-l", &flat]) .output() .is_ok(); if !ok { return false; } thread::sleep(Duration::from_millis(200)); // Submit Command::new("tmux") .args(["send-keys", "-t", pane, "Enter"]) .output() .is_ok() }