Add fancy ASCII art header for agent mode

The agent mode header now shows:
- Agent name in uppercase with box art
- Working directory (truncated if too long)
- Status indicators for README, AGENTS.md, and Memory loading
- Task preview if provided

Also exports truncate_for_display and adds truncate_path_for_display
helper functions in project_files module.
This commit is contained in:
Dhanji R. Prasanna
2026-01-11 17:11:14 +05:30
parent 2fbdac7aa9
commit 08747595a1
2 changed files with 51 additions and 11 deletions

View File

@@ -28,7 +28,7 @@ use tracing::{debug, error};
use g3_core::error_handling::{classify_error, ErrorType, RecoverableError};
use machine_ui_writer::MachineUiWriter;
use metrics::{format_elapsed_time, generate_turn_histogram, TurnMetrics};
use project_files::{extract_readme_heading, read_agents_config, read_project_memory, read_project_readme};
use project_files::{extract_readme_heading, read_agents_config, read_project_memory, read_project_readme, truncate_for_display, truncate_path_for_display};
use simple_output::SimpleOutput;
use ui_writer_impl::ConsoleUiWriter;
@@ -539,9 +539,6 @@ async fn run_agent_mode(
))?
};
output.print(&format!("🤖 Running as agent: {}", agent_name));
output.print(&format!("📁 Working directory: {:?}", workspace_dir));
// Load config
let mut config = g3_config::Config::load(config_path)?;
@@ -562,20 +559,23 @@ async fn run_agent_mode(
let system_prompt = get_agent_system_prompt(&agent_prompt, true);
// Load AGENTS.md, README, and memory - same as normal mode
let agents_content = read_agents_config(&workspace_dir);
let readme_content = read_project_readme(&workspace_dir);
let memory_content = read_project_memory(&workspace_dir);
let agents_content_opt = read_agents_config(&workspace_dir);
let readme_content_opt = read_project_readme(&workspace_dir);
let memory_content_opt = read_project_memory(&workspace_dir);
let has_agents = agents_content_opt.is_some();
let has_readme = readme_content_opt.is_some();
let has_memory = memory_content_opt.is_some();
// Combine all content for the agent's context
let combined_content = {
let mut parts = Vec::new();
if let Some(agents) = agents_content {
if let Some(agents) = agents_content_opt {
parts.push(agents);
}
if let Some(readme) = readme_content {
if let Some(readme) = readme_content_opt {
parts.push(readme);
}
if let Some(memory) = memory_content {
if let Some(memory) = memory_content_opt {
parts.push(memory);
}
if parts.is_empty() {
@@ -585,6 +585,31 @@ async fn run_agent_mode(
}
};
// Print fancy agent header
let agent_upper = agent_name.to_uppercase();
output.print("");
output.print("┌─────────────────────────────────────────────────────────┐");
output.print(&format!("│ 🤖 AGENT: {:<46}", agent_upper));
output.print("├─────────────────────────────────────────────────────────┤");
output.print(&format!("│ 📁 {:<53}",
truncate_path_for_display(&workspace_dir, 53)));
output.print("├─────────────────────────────────────────────────────────┤");
// Show what was loaded
let readme_status = if has_readme { "" } else { "·" };
let agents_status = if has_agents { "" } else { "·" };
let memory_status = if has_memory { "" } else { "·" };
output.print(&format!("{} README {} AGENTS.md {} Memory │",
readme_status, agents_status, memory_status));
output.print("└─────────────────────────────────────────────────────────┘");
output.print("");
// Show task if provided
if let Some(ref task) = task {
output.print(&format!("📋 Task: {}", truncate_for_display(task, 60)));
output.print("");
}
// Create agent with custom system prompt
let ui_writer = ConsoleUiWriter::new();
// Set agent mode on UI writer for visual differentiation (light gray tool names)

View File

@@ -140,7 +140,8 @@ fn find_fallback_title(content: &str) -> Option<String> {
}
/// Truncate a string for display, adding ellipsis if needed.
fn truncate_for_display(s: &str, max_len: usize) -> String {
/// Used for displaying long strings in fixed-width UI elements.
pub fn truncate_for_display(s: &str, max_len: usize) -> String {
if s.len() > max_len {
format!("{}...", &s[..max_len - 3])
} else {
@@ -148,6 +149,20 @@ fn truncate_for_display(s: &str, max_len: usize) -> String {
}
}
/// Truncate a path for display, showing the end portion if too long.
/// For paths, we want to show the most relevant part (the end).
pub fn truncate_path_for_display(path: &std::path::Path, max_len: usize) -> String {
let path_str = path.display().to_string();
if path_str.len() > max_len {
// Show the end of the path with ... prefix
let start = path_str.len() - (max_len - 3);
// Find a path separator to break at cleanly if possible
format!("...{}", &path_str[start..])
} else {
path_str
}
}
#[cfg(test)]
mod tests {
use super::*;