Function bodies 848 total
test_debounce_resets_on_different_state function · rust · L1022-L1038 (17 LOC)src/agent/detector.rs
fn test_debounce_resets_on_different_state() {
let mut debounce = DetectionDebounce::new();
let current = AgentState::Running { since: Utc::now() };
// First tick: detected WaitingForInput
debounce.process(
AgentState::WaitingForInput {
prompt_type: PromptType::InputPrompt,
since: Utc::now(),
},
¤t,
);
// Second tick: detected Idle instead — debounce resets
let result = debounce.process(AgentState::Idle { since: Utc::now() }, ¤t);
assert!(result.is_none()); // Reset, not enough ticks for Idle
}test_debounce_terminal_state_immediate function · rust · L1041-L1052 (12 LOC)src/agent/detector.rs
fn test_debounce_terminal_state_immediate() {
let mut debounce = DetectionDebounce::new();
let current = AgentState::Running { since: Utc::now() };
let completed = AgentState::Completed {
at: Utc::now(),
exit_code: Some(0),
};
// Terminal states bypass debounce
let result = debounce.process(completed, ¤t);
assert!(result.is_some());
}test_debounce_same_as_current_no_transition function · rust · L1055-L1062 (8 LOC)src/agent/detector.rs
fn test_debounce_same_as_current_no_transition() {
let mut debounce = DetectionDebounce::new();
let current = AgentState::Running { since: Utc::now() };
// Detecting same state as current — no transition needed
let result = debounce.process(AgentState::Running { since: Utc::now() }, ¤t);
assert!(result.is_none());
}test_debounce_custom_threshold function · rust · L1065-L1073 (9 LOC)src/agent/detector.rs
fn test_debounce_custom_threshold() {
let mut debounce = DetectionDebounce::with_threshold(3);
let current = AgentState::Running { since: Utc::now() };
let idle = AgentState::Idle { since: Utc::now() };
assert!(debounce.process(idle.clone(), ¤t).is_none()); // tick 1
assert!(debounce.process(idle.clone(), ¤t).is_none()); // tick 2
assert!(debounce.process(idle, ¤t).is_some()); // tick 3 — transition
}test_debounce_default_impl function · rust · L1076-L1081 (6 LOC)src/agent/detector.rs
fn test_debounce_default_impl() {
let debounce = DetectionDebounce::default();
assert_eq!(debounce.threshold, 2);
assert_eq!(debounce.count, 0);
assert!(debounce.pending.is_none());
}new function · rust · L86-L120 (35 LOC)src/agent/handle.rs
pub fn new(
id: AgentId,
name: String,
project_name: String,
pty: PtyController,
parser: vt100::Parser,
child: Box<dyn Child + Send + Sync>,
spawn_command: String,
spawn_args: Vec<String>,
spawn_cwd: PathBuf,
spawn_env: HashMap<String, String>,
session_id: Option<String>,
) -> Self {
Self {
id,
name,
project_name,
state: AgentState::Spawning { since: Utc::now() },
pty,
parser,
child,
spawned_at: Utc::now(),
last_output_at: None,
debounce: DetectionDebounce::new(),
dirty: true,
spawn_command,
spawn_args,
spawn_cwd,
spawn_env,
scrollback: ScrollbackBuffer::new(10 * 1024 * 1024), // 10MB default
session_id,
resume_retry_attempted: false,
has_unread_result: false,id function · rust · L123-L126 (4 LOC)src/agent/handle.rs
pub fn id(&self) -> AgentId {
self.id
}Repobility · severity-and-effort ranking · https://repobility.com
name function · rust · L127-L130 (4 LOC)src/agent/handle.rs
pub fn name(&self) -> &str {
&self.name
}set_name function · rust · L133-L136 (4 LOC)src/agent/handle.rs
pub fn set_name(&mut self, new_name: String) {
self.name = new_name;
self.dirty = true;
}project_name function · rust · L137-L140 (4 LOC)src/agent/handle.rs
pub fn project_name(&self) -> &str {
&self.project_name
}set_project_name function · rust · L141-L145 (5 LOC)src/agent/handle.rs
pub fn set_project_name(&mut self, new_name: String) {
self.project_name = new_name;
self.dirty = true;
}session_id function · rust · L146-L149 (4 LOC)src/agent/handle.rs
pub fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
}resume_retry_attempted function · rust · L150-L153 (4 LOC)src/agent/handle.rs
pub fn resume_retry_attempted(&self) -> bool {
self.resume_retry_attempted
}set_resume_retry_attempted function · rust · L154-L157 (4 LOC)src/agent/handle.rs
pub fn set_resume_retry_attempted(&mut self, val: bool) {
self.resume_retry_attempted = val;
}has_unread_result function · rust · L160-L162 (3 LOC)src/agent/handle.rs
pub fn has_unread_result(&self) -> bool {
self.has_unread_result
}Repobility · MCP-ready · https://repobility.com
mark_result_read function · rust · L165-L167 (3 LOC)src/agent/handle.rs
pub fn mark_result_read(&mut self) {
self.has_unread_result = false;
}is_stale_resume_failure function · rust · L173-L185 (13 LOC)src/agent/handle.rs
pub fn is_stale_resume_failure(&self, max_secs: u64) -> bool {
if self.resume_retry_attempted {
return false;
}
if !matches!(self.state, AgentState::Errored { .. }) {
return false;
}
let age = (Utc::now() - self.spawned_at).num_seconds();
if age > max_secs as i64 {
return false;
}
self.spawn_args.iter().any(|a| a == "--resume" || a == "-r")
}state function · rust · L186-L189 (4 LOC)src/agent/handle.rs
pub fn state(&self) -> &AgentState {
&self.state
}spawned_at function · rust · L190-L193 (4 LOC)src/agent/handle.rs
pub fn spawned_at(&self) -> DateTime<Utc> {
self.spawned_at
}is_dirty function · rust · L194-L197 (4 LOC)src/agent/handle.rs
pub fn is_dirty(&self) -> bool {
self.dirty
}screen function · rust · L200-L202 (3 LOC)src/agent/handle.rs
pub fn screen(&self) -> &vt100::Screen {
self.parser.screen()
}mark_clean function · rust · L205-L207 (3 LOC)src/agent/handle.rs
pub fn mark_clean(&mut self) {
self.dirty = false;
}uptime function · rust · L210-L220 (11 LOC)src/agent/handle.rs
pub fn uptime(&self) -> String {
let elapsed = Utc::now() - self.spawned_at;
let secs = elapsed.num_seconds();
if secs < 60 {
format!("{secs}s")
} else if secs < 3600 {
format!("{}m {}s", secs / 60, secs % 60)
} else {
format!("{}h {}m", secs / 3600, (secs % 3600) / 60)
}
}Repobility · code-quality intelligence · https://repobility.com
process_output function · rust · L226-L237 (12 LOC)src/agent/handle.rs
pub fn process_output(&mut self, data: &[u8]) {
self.parser.process(data);
self.scrollback.append(data);
// Auto-scroll to bottom when new output arrives (if already at bottom)
if !self.scrollback.is_scrolled() {
self.scrollback.scroll_to_bottom();
}
self.last_output_at = Some(Utc::now());
self.dirty = true;
}write_input function · rust · L240-L242 (3 LOC)src/agent/handle.rs
pub fn write_input(&self, data: &[u8]) -> Result<()> {
self.pty.write(data)
}resize function · rust · L245-L251 (7 LOC)src/agent/handle.rs
pub fn resize(&mut self, size: PtySize) {
if let Err(e) = self.pty.resize(size) {
tracing::warn!("Failed to resize PTY for agent {}: {}", self.name, e);
}
self.parser.screen_mut().set_size(size.rows, size.cols);
self.dirty = true;
}scrollback function · rust · L256-L258 (3 LOC)src/agent/handle.rs
pub fn scrollback(&self) -> &ScrollbackBuffer {
&self.scrollback
}scrollback_mut function · rust · L261-L263 (3 LOC)src/agent/handle.rs
pub fn scrollback_mut(&mut self) -> &mut ScrollbackBuffer {
&mut self.scrollback
}scroll_up function · rust · L266-L275 (10 LOC)src/agent/handle.rs
pub fn scroll_up(&mut self, page_height: usize) {
self.scrollback.scroll_up(page_height);
// Discover actual max scrollback: set_scrollback(usize::MAX) clamps
// to the real scrollback length, then we read it back and reset.
self.parser.screen_mut().set_scrollback(usize::MAX);
let max_scrollback = self.parser.screen().scrollback();
self.parser.screen_mut().set_scrollback(0);
self.scrollback.clamp_scroll(max_scrollback);
self.dirty = true;
}set_scrollback_view function · rust · L282-L284 (3 LOC)src/agent/handle.rs
pub fn set_scrollback_view(&mut self, offset: usize) {
self.parser.screen_mut().set_scrollback(offset);
}scroll_down function · rust · L287-L290 (4 LOC)src/agent/handle.rs
pub fn scroll_down(&mut self, page_height: usize) {
self.scrollback.scroll_down(page_height);
self.dirty = true;
}Repobility — the code-quality scanner for AI-generated software · https://repobility.com
mouse_scroll_up function · rust · L293-L300 (8 LOC)src/agent/handle.rs
pub fn mouse_scroll_up(&mut self, lines: usize) {
self.scrollback.mouse_scroll_up(lines);
self.parser.screen_mut().set_scrollback(usize::MAX);
let max_scrollback = self.parser.screen().scrollback();
self.parser.screen_mut().set_scrollback(0);
self.scrollback.clamp_scroll(max_scrollback);
self.dirty = true;
}mouse_scroll_down function · rust · L303-L306 (4 LOC)src/agent/handle.rs
pub fn mouse_scroll_down(&mut self, lines: usize) {
self.scrollback.mouse_scroll_down(lines);
self.dirty = true;
}scroll_offset function · rust · L309-L311 (3 LOC)src/agent/handle.rs
pub fn scroll_offset(&self) -> usize {
self.scrollback.scroll_offset()
}is_scrolled function · rust · L314-L316 (3 LOC)src/agent/handle.rs
pub fn is_scrolled(&self) -> bool {
self.scrollback.is_scrolled()
}start_search function · rust · L319-L325 (7 LOC)src/agent/handle.rs
pub fn start_search(&mut self, query: &str) {
self.scrollback.start_search(query);
if let Some(search) = self.scrollback.search_mut() {
search.search(self.parser.screen());
}
self.dirty = true;
}clear_search function · rust · L328-L331 (4 LOC)src/agent/handle.rs
pub fn clear_search(&mut self) {
self.scrollback.clear_search();
self.dirty = true;
}search_next function · rust · L334-L343 (10 LOC)src/agent/handle.rs
pub fn search_next(&mut self) -> Option<usize> {
let line = self
.scrollback
.search_mut()
.and_then(|s| s.next_match().map(|m| m.line));
if line.is_some() {
self.dirty = true;
}
line
}search_prev function · rust · L346-L355 (10 LOC)src/agent/handle.rs
pub fn search_prev(&mut self) -> Option<usize> {
let line = self
.scrollback
.search_mut()
.and_then(|s| s.prev_match().map(|m| m.line));
if line.is_some() {
self.dirty = true;
}
line
}Repobility · severity-and-effort ranking · https://repobility.com
detect_and_update function · rust · L361-L409 (49 LOC)src/agent/handle.rs
pub fn detect_and_update(
&mut self,
patterns: &DetectionPatterns,
idle_timeout_secs: u64,
) -> Option<AgentState> {
// Check process exit
let process_exited = match self.child.try_wait() {
Ok(Some(status)) => Some(ProcessExit {
exit_code: Some(status.exit_code() as i32),
signal: status.signal().is_some(),
}),
Ok(None) => None,
Err(e) => {
tracing::warn!("try_wait failed for agent {}: {}", self.name, e);
None
}
};
// Calculate seconds since last output
let seconds_since_output = match self.last_output_at {
Some(last) => (Utc::now() - last).num_milliseconds() as f64 / 1000.0,
None => 0.0,
};
// Extract screen lines
let screen_lines = extract_screen_lines(self.parser.screen(), patterns.scan_lines());
let signals = DetectionSignals {
kill function · rust · L420-L466 (47 LOC)src/agent/handle.rs
pub fn kill(&mut self) {
// Step 1: Try graceful shutdown by sending Ctrl-C through the PTY.
// This is how a terminal user would interrupt a process.
let _ = self.pty.write(b"\x03");
// Step 2: Send SIGTERM to the process group for a clean exit.
// The child was spawned with setsid(), so its PID == PGID.
// Using negative PID sends the signal to the entire process group,
// ensuring Electron's child processes also receive it.
if let Some(pid) = self.child.process_id() {
unsafe {
libc::kill(-(pid as i32), libc::SIGTERM);
}
}
// Step 3: Give the process a generous grace period to exit cleanly.
// Electron needs more time than the 250ms that portable-pty allows.
for _ in 0..20 {
match self.child.try_wait() {
Ok(Some(_)) => {
self.pty.shutdown();
self.state = AgentState::Errored {
scrollback_raw_bytes function · rust · L469-L471 (3 LOC)src/agent/handle.rs
pub fn scrollback_raw_bytes(&self) -> &[u8] {
self.scrollback.raw_bytes()
}load_scrollback_history function · rust · L478-L481 (4 LOC)src/agent/handle.rs
pub fn load_scrollback_history(&mut self, data: &[u8]) {
self.scrollback.append(data);
self.dirty = true;
}restart_params function · rust · L484-L494 (11 LOC)src/agent/handle.rs
pub fn restart_params(&self) -> RestartParams {
RestartParams {
name: self.name.clone(),
project_name: self.project_name.clone(),
command: self.spawn_command.clone(),
args: self.spawn_args.clone(),
cwd: self.spawn_cwd.clone(),
env: self.spawn_env.clone(),
session_id: self.session_id.clone(),
}
}test_uptime_formatting_seconds function · rust · L511-L515 (5 LOC)src/agent/handle.rs
fn test_uptime_formatting_seconds() {
// Test seconds-only range
let fmt = format_duration(30);
assert_eq!(fmt, "30s");
}test_uptime_formatting_minutes function · rust · L518-L521 (4 LOC)src/agent/handle.rs
fn test_uptime_formatting_minutes() {
let fmt = format_duration(125);
assert_eq!(fmt, "2m 5s");
}test_uptime_formatting_hours function · rust · L524-L527 (4 LOC)src/agent/handle.rs
fn test_uptime_formatting_hours() {
let fmt = format_duration(3661);
assert_eq!(fmt, "1h 1m");
}Repobility · MCP-ready · https://repobility.com
format_duration function · rust · L530-L538 (9 LOC)src/agent/handle.rs
fn format_duration(secs: i64) -> String {
if secs < 60 {
format!("{secs}s")
} else if secs < 3600 {
format!("{}m {}s", secs / 60, secs % 60)
} else {
format!("{}h {}m", secs / 3600, (secs % 3600) / 60)
}
}new function · rust · L43-L54 (12 LOC)src/agent/manager.rs
pub fn new(config: &MaestroConfig, event_tx: mpsc::UnboundedSender<AppEvent>) -> Self {
let detection_patterns = DetectionPatterns::from_config(&config.detection);
Self {
agents: HashMap::new(),
display_order: Vec::new(),
event_tx,
detection_patterns,
max_agents: config.global.max_agents,
config: config.clone(),
}
}spawn function · rust · L60-L157 (98 LOC)src/agent/manager.rs
pub fn spawn(
&mut self,
name: String,
project_name: String,
command: String,
args: Vec<String>,
cwd: std::path::PathBuf,
env: HashMap<String, String>,
pty_size: PtySize,
) -> Result<AgentId> {
// Check agent limit
let alive_count = self
.agents
.values()
.filter(|a| a.state().is_alive())
.count();
if alive_count >= self.max_agents {
bail!(
"Agent limit reached ({}/{}). Kill an agent first.",
alive_count,
self.max_agents,
);
}
// Check for duplicate name within same project
if self.find_by_name(&project_name, &name).is_some() {
bail!(
"Agent '{}' already exists in project '{}'",
name,
project_name
);
}
// For Claude commands, generate a session ID and inject --s