diff options
Diffstat (limited to 'crates/dinggerd/src/names.rs')
| -rw-r--r-- | crates/dinggerd/src/names.rs | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/crates/dinggerd/src/names.rs b/crates/dinggerd/src/names.rs new file mode 100644 index 0000000..ef09988 --- /dev/null +++ b/crates/dinggerd/src/names.rs @@ -0,0 +1,117 @@ +//! Agent display names: what the agent is working on, taken from the +//! terminal title the agent sets (herdr's `terminal_title_stripped`, plus +//! pi's title convention). + +use detect_core::Agent; + +/// herdr's activity glyphs (src/terminal/title.rs): claude prefixes its title +/// with one of these, or a braille spinner frame. +const ACTIVITY_GLYPHS: &str = "·✢✳✶✻✽◐◓◑◒"; + +/// herdr's `stripped_terminal_title`: drop one recognized leading activity +/// glyph and the whitespace after it. +fn stripped_title(title: &str) -> &str { + let title = title.trim(); + let mut chars = title.chars(); + let Some(first) = chars.next() else { + return title; + }; + let rest = chars.as_str(); + let recognized = matches!(first, '\u{2800}'..='\u{28ff}') || ACTIVITY_GLYPHS.contains(first); + if recognized && (rest.is_empty() || rest.starts_with(char::is_whitespace)) { + rest.trim() + } else { + title + } +} + +/// The agent's session/task name, if its title carries one. +/// +/// - pi titles itself `π - <session name> - <cwd basename>`, or +/// `π - <cwd basename>` when the session has no name. +/// - other agents (claude) use the title as the task, behind an activity +/// glyph. +/// - tmux's default title is the host name: no name. +pub fn display_name(agent: Option<Agent>, title: &str, cwd: &str, host: &str) -> Option<String> { + let title = stripped_title(title); + if title.is_empty() || title == host || host.split('.').next() == Some(title) { + return None; + } + let name = match agent { + Some(Agent::Pi) => { + let rest = title.strip_prefix("π - ")?; + let dir = cwd.trim_end_matches('/').rsplit('/').next().unwrap_or(""); + if !dir.is_empty() && rest == dir { + return None; + } + rest.strip_suffix(&format!(" - {dir}")).filter(|_| !dir.is_empty()).unwrap_or(rest) + } + // claude's title before it names a task is just the product name. + Some(Agent::Claude) if title == "Claude Code" => return None, + _ => title, + }; + let name = name.trim(); + (!name.is_empty()).then(|| name.to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + const HOST: &str = "Igor-Soarez-H3H1W71DJ0"; + + #[test] + fn pi_session_name_between_prefix_and_directory() { + assert_eq!( + display_name(Some(Agent::Pi), "π - Ship langgraph 1.2.13 release - x", "/Users/sz/x", HOST), + Some("Ship langgraph 1.2.13 release".into()) + ); + assert_eq!(display_name(Some(Agent::Pi), "π - 9103 - x", "/Users/sz/x", HOST), Some("9103".into())); + } + + #[test] + fn pi_without_a_session_name_has_no_name() { + assert_eq!(display_name(Some(Agent::Pi), "π - x", "/Users/sz/x", HOST), None); + } + + #[test] + fn pi_name_may_contain_the_separator() { + assert_eq!( + display_name(Some(Agent::Pi), "π - Fix a - b split - dot", "/Users/sz/x/dot", HOST), + Some("Fix a - b split".into()) + ); + } + + #[test] + fn pi_title_after_cd_keeps_the_rest() { + assert_eq!( + display_name(Some(Agent::Pi), "π - Task - olddir", "/Users/sz/newdir", HOST), + Some("Task - olddir".into()) + ); + } + + #[test] + fn claude_title_loses_its_activity_glyph() { + assert_eq!( + display_name(Some(Agent::Claude), "✳ Investigate flaky test", "/tmp", HOST), + Some("Investigate flaky test".into()) + ); + assert_eq!( + display_name(Some(Agent::Claude), "⠋ Investigate flaky test", "/tmp", HOST), + Some("Investigate flaky test".into()) + ); + } + + #[test] + fn claude_default_title_is_not_a_name() { + assert_eq!(display_name(Some(Agent::Claude), "✳ Claude Code", "/tmp", HOST), None); + assert_eq!(display_name(Some(Agent::Claude), "Claude Code", "/tmp", HOST), None); + } + + #[test] + fn default_tmux_title_is_not_a_name() { + assert_eq!(display_name(Some(Agent::Claude), HOST, "/tmp", HOST), None); + assert_eq!(display_name(Some(Agent::Claude), "Igor-Soarez-H3H1W71DJ0", "/tmp", "Igor-Soarez-H3H1W71DJ0.local"), None); + assert_eq!(display_name(Some(Agent::Claude), "", "/tmp", HOST), None); + } +} |
