Move API code from user/ to agent/
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
021eafe6da
commit
9bebbcb635
22 changed files with 259 additions and 251 deletions
132
src/user/tui/subconscious.rs
Normal file
132
src/user/tui/subconscious.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// subconscious_screen.rs — F3 subconscious agent overlay
|
||||
//
|
||||
// Shows agent list with status indicators, and a detail view
|
||||
// with log tail for the selected agent.
|
||||
|
||||
use ratatui::{
|
||||
layout::Rect,
|
||||
style::{Color, Modifier, Style},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Paragraph, Wrap},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use super::{App, SUBCONSCIOUS_AGENTS, SCREEN_LEGEND};
|
||||
|
||||
impl App {
|
||||
pub(crate) fn draw_agents(&self, frame: &mut Frame, size: Rect) {
|
||||
let output_dir = crate::store::memory_dir().join("agent-output");
|
||||
|
||||
if self.agent_log_view {
|
||||
self.draw_agent_log(frame, size, &output_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut lines: Vec<Line> = Vec::new();
|
||||
let section = Style::default().fg(Color::Yellow);
|
||||
let _dim = Style::default().fg(Color::DarkGray);
|
||||
let hint = Style::default().fg(Color::DarkGray).add_modifier(Modifier::ITALIC);
|
||||
|
||||
lines.push(Line::raw(""));
|
||||
lines.push(Line::styled("── Subconscious Agents ──", section));
|
||||
lines.push(Line::styled(" (↑/↓ select, Enter/→ view log, Esc back)", hint));
|
||||
lines.push(Line::raw(""));
|
||||
|
||||
for (i, &name) in SUBCONSCIOUS_AGENTS.iter().enumerate() {
|
||||
let selected = i == self.agent_selected;
|
||||
let prefix = if selected { "▸ " } else { " " };
|
||||
let bg = if selected { Style::default().bg(Color::DarkGray) } else { Style::default() };
|
||||
|
||||
let agent = self.agent_state.iter().find(|a| a.name == name);
|
||||
|
||||
match agent.and_then(|a| a.pid) {
|
||||
Some(pid) => {
|
||||
let phase = agent.and_then(|a| a.phase.as_deref()).unwrap_or("?");
|
||||
lines.push(Line::from(vec![
|
||||
Span::styled(format!("{}{:<20}", prefix, name), bg.fg(Color::Green)),
|
||||
Span::styled("● ", bg.fg(Color::Green)),
|
||||
Span::styled(format!("pid {} phase: {}", pid, phase), bg),
|
||||
]));
|
||||
}
|
||||
None => {
|
||||
lines.push(Line::from(vec![
|
||||
Span::styled(format!("{}{:<20}", prefix, name), bg.fg(Color::Gray)),
|
||||
Span::styled("○ idle", bg.fg(Color::DarkGray)),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let block = Block::default()
|
||||
.title_top(Line::from(SCREEN_LEGEND).left_aligned())
|
||||
.title_top(Line::from(" subconscious ").right_aligned())
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Cyan));
|
||||
|
||||
let para = Paragraph::new(lines)
|
||||
.block(block)
|
||||
.scroll((self.debug_scroll, 0));
|
||||
frame.render_widget(para, size);
|
||||
}
|
||||
|
||||
fn draw_agent_log(&self, frame: &mut Frame, size: Rect, _output_dir: &std::path::Path) {
|
||||
let name = SUBCONSCIOUS_AGENTS.get(self.agent_selected).unwrap_or(&"?");
|
||||
let agent = self.agent_state.iter().find(|a| a.name == *name);
|
||||
let mut lines: Vec<Line> = Vec::new();
|
||||
let section = Style::default().fg(Color::Yellow);
|
||||
let hint = Style::default().fg(Color::DarkGray).add_modifier(Modifier::ITALIC);
|
||||
|
||||
lines.push(Line::raw(""));
|
||||
lines.push(Line::styled(format!("── {} ──", name), section));
|
||||
lines.push(Line::styled(" (Esc/← back, PgUp/PgDn scroll)", hint));
|
||||
lines.push(Line::raw(""));
|
||||
|
||||
// Show pid status from state
|
||||
match agent.and_then(|a| a.pid) {
|
||||
Some(pid) => {
|
||||
let phase = agent.and_then(|a| a.phase.as_deref()).unwrap_or("?");
|
||||
lines.push(Line::from(vec![
|
||||
Span::styled(" Status: ", Style::default()),
|
||||
Span::styled(format!("● running pid {} phase: {}", pid, phase),
|
||||
Style::default().fg(Color::Green)),
|
||||
]));
|
||||
}
|
||||
None => {
|
||||
lines.push(Line::styled(" Status: idle", Style::default().fg(Color::DarkGray)));
|
||||
}
|
||||
}
|
||||
|
||||
// Show log path
|
||||
if let Some(log_path) = agent.and_then(|a| a.log_path.as_ref()) {
|
||||
lines.push(Line::raw(format!(" Log: {}", log_path.display())));
|
||||
}
|
||||
lines.push(Line::raw(""));
|
||||
|
||||
// Show agent log tail
|
||||
lines.push(Line::styled("── Agent Log ──", section));
|
||||
if let Some(content) = agent
|
||||
.and_then(|a| a.log_path.as_ref())
|
||||
.and_then(|p| std::fs::read_to_string(p).ok())
|
||||
{
|
||||
let log_lines: Vec<&str> = content.lines().collect();
|
||||
let start = log_lines.len().saturating_sub(40);
|
||||
for line in &log_lines[start..] {
|
||||
lines.push(Line::raw(format!(" {}", line)));
|
||||
}
|
||||
} else {
|
||||
lines.push(Line::styled(" (no log available)", hint));
|
||||
}
|
||||
|
||||
let block = Block::default()
|
||||
.title_top(Line::from(SCREEN_LEGEND).left_aligned())
|
||||
.title_top(Line::from(format!(" {} ", name)).right_aligned())
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Cyan));
|
||||
|
||||
let para = Paragraph::new(lines)
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: false })
|
||||
.scroll((self.debug_scroll, 0));
|
||||
frame.render_widget(para, size);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue