pub fn contains_any(pane: &str, markers: &[&str]) -> bool {
markers.iter().any(|m| pane.contains(m))
}
pub fn parse_menu_options(pane: &str) -> Vec<String> {
let mut options = Vec::new();
let mut expected = 1u32;
for line in pane.lines() {
let trimmed = line.trim_start_matches(['❯', '>', ' ', '\t']);
let Some((num, rest)) = trimmed.split_once(". ") else {
continue;
};
if num.parse::<u32>() == Ok(expected) {
options.push(rest.trim().to_string());
expected += 1;
}
}
options
}
#[cfg(test)]
mod tests {
use super::*;
const MENU: &str = "\
Claude has written up a plan and is ready to execute. Would you like to proceed?
❯ 1. Yes, and use auto mode
2. Yes, manually approve edits
3. No, refine with Ultraplan on Claude Code on the web
4. Tell Claude what to change";
#[test]
fn parses_numbered_menu() {
let opts = parse_menu_options(MENU);
assert_eq!(opts.len(), 4);
assert_eq!(opts[0], "Yes, and use auto mode");
assert_eq!(opts[3], "Tell Claude what to change");
}
#[test]
fn detects_marker() {
assert!(contains_any(MENU, &["Would you like to proceed?"]));
assert!(!contains_any(MENU, &["No such marker"]));
}
const PERMISSION_GATE: &str = "\
Bash command
cp /etc/hostname /tmp/csd-perm/hn.txt
Copy hostname file to working directory
Do you want to proceed?
❯ 1. Yes
2. Yes, and always allow access to etc/ from this project
3. No
Esc to cancel · Tab to amend · ctrl+e to explain";
#[test]
fn parses_live_permission_menu() {
assert!(contains_any(PERMISSION_GATE, &["Do you want to proceed?"]));
let opts = parse_menu_options(PERMISSION_GATE);
assert_eq!(opts.len(), 3);
assert_eq!(opts[0], "Yes");
assert_eq!(opts[1], "Yes, and always allow access to etc/ from this project");
assert_eq!(opts[2], "No");
}
}