refactor(cli): extract display utilities to eliminate code duplication

Created display.rs module with shared display functions:
- format_workspace_path() / print_workspace_path()
- LoadedContent struct for tracking loaded project files
- print_loaded_status() for status line display
- print_project_heading() for README heading

Updated interactive.rs and agent_mode.rs to use the new module,
eliminating duplicated workspace path formatting and loaded items
status line logic.

Results:
- interactive.rs: 641 → 595 lines (-46)
- agent_mode.rs: 312 → 288 lines (-24)
- New display.rs: 197 lines with 5 unit tests

Agent: fowler
This commit is contained in:
Dhanji R. Prasanna
2026-01-20 14:22:46 +05:30
parent ecea49d328
commit 710c54105b
4 changed files with 221 additions and 93 deletions

View File

@@ -11,6 +11,7 @@ use tracing::{debug, error};
use g3_core::ui_writer::UiWriter;
use g3_core::Agent;
use crate::display::{LoadedContent, print_loaded_status, print_project_heading, print_workspace_path};
use crate::g3_status::{G3Status, Status};
use crate::project_files::extract_readme_heading;
use crate::simple_output::SimpleOutput;
@@ -100,69 +101,22 @@ pub async fn run_interactive<W: UiWriter>(
}
}
// Display message if AGENTS.md or README was loaded
// Display message if AGENTS.md or README was loaded
if let Some(ref content) = combined_content {
// Check what was loaded
let has_agents = content.contains("Agent Configuration");
let has_readme = content.contains("Project README");
let has_include_prompt = content.contains("Included Prompt");
let has_memory = content.contains("=== Project Memory");
let loaded = LoadedContent::from_combined_content(content);
// Extract project name if README is loaded
let project_name = if has_readme {
// Extract the first heading or title from the README
extract_readme_heading(content)
} else {
None
};
if let Some(name) = project_name {
print!("{}>> {}{}\n", SetForegroundColor(Color::DarkGrey), name, ResetColor);
if loaded.has_readme {
if let Some(name) = extract_readme_heading(content) {
print_project_heading(&name);
}
}
// Build status line showing only what was loaded (in load order)
let mut loaded_items: Vec<&str> = Vec::new();
if has_readme {
loaded_items.push("README");
}
if has_agents {
loaded_items.push("AGENTS.md");
}
if has_include_prompt {
loaded_items.push("prompt");
}
if has_memory {
loaded_items.push("Memory");
}
// Print status line only if something was loaded
if !loaded_items.is_empty() {
let status_str = loaded_items.iter().map(|s| format!("{}", s)).collect::<Vec<_>>().join(" ");
print!(
"{} {}{}\n",
SetForegroundColor(Color::DarkGrey),
status_str,
ResetColor
);
}
print_loaded_status(&loaded);
}
// Display workspace path
let workspace_display = {
let path_str = workspace_path.display().to_string();
dirs::home_dir()
.and_then(|home| {
path_str
.strip_prefix(&home.display().to_string())
.map(|s| format!("~{}", s))
})
.unwrap_or(path_str)
};
print!(
"{}-> {}{}\n",
SetForegroundColor(Color::DarkGrey),
workspace_display,
ResetColor
);
print_workspace_path(workspace_path);
output.print("");
}