Fix planner UI whitespace and workspace logs directory

Resolve two critical issues in planner mode that persisted through
multiple fix attempts:

1. Remove excessive whitespace between tool call displays by replacing
   direct println!() calls with ui_writer methods and eliminating
   redundant newlines in agent response streaming.

2. Ensure all log files (errors, sessions, tool calls, context dumps)
   are written to <workspace>/logs instead of codepath by properly
   initializing G3_WORKSPACE_PATH from --workspace argument.
This commit is contained in:
Jochen
2025-12-10 16:18:49 +11:00
parent a03a432963
commit 87bceba54f
8 changed files with 310 additions and 20 deletions

View File

@@ -256,9 +256,8 @@ impl g3_core::ui_writer::UiWriter for PlannerUiWriter {
"{}".to_string()
};
// Print on EXACTLY one line, no trailing newline, use println! with explicit single line format
println!("🔧 [{}] {} {}", count, tool_name, args_display);
std::io::stdout().flush().ok();
// Print on EXACTLY one line using ui_writer.println
self.println(&format!("🔧 [{}] {} {}", count, tool_name, args_display));
}
fn print_tool_arg(&self, _key: &str, _value: &str) {}
@@ -269,15 +268,17 @@ impl g3_core::ui_writer::UiWriter for PlannerUiWriter {
fn print_tool_timing(&self, _duration_str: &str) {}
fn print_agent_prompt(&self) {
// Clear any status line before agent response
print!("\r{:<80}\n", "");
// No-op - don't add extra blank lines
}
fn print_agent_response(&self, content: &str) {
// Display non-tool text messages from LLM
if !content.trim().is_empty() {
// Ensure we're on a fresh line, print content as-is, no buffering
print!("{}", content);
// Display non-tool text messages from LLM without adding extra newlines
let trimmed = content.trim_end();
if !trimmed.is_empty() {
// Strip ALL trailing whitespace and DON'T add any back.
// Tool headers already use println!() which adds their own newline.
// Adding newlines here causes cumulative blank lines between tool calls.
print!("{}", trimmed);
std::io::stdout().flush().ok();
}
}
@@ -309,7 +310,11 @@ impl g3_core::ui_writer::UiWriter for PlannerUiWriter {
pub async fn call_refinement_llm_with_tools(
config: &Config,
codepath: &str,
workspace: &str,
) -> Result<String> {
eprintln!("[DEBUG] call_refinement_llm_with_tools: codepath={}, workspace={}",
codepath, workspace);
// Build system message with codepath context
let system_prompt = prompts::REFINE_REQUIREMENTS_SYSTEM_PROMPT
.replace("<codepath>", codepath);
@@ -321,12 +326,22 @@ pub async fn call_refinement_llm_with_tools(
let planner_config = config.for_planner()?;
let ui_writer = PlannerUiWriter::new();
// Create project pointing to codepath as workspace
let workspace = std::path::PathBuf::from(codepath);
let project = Project::new(workspace.clone());
// CRITICAL FIX: Use the actual workspace directory, NOT codepath!
// The workspace is where logs should be written (e.g., /tmp/g3_test_workspace)
// The codepath is where the source code lives (e.g., ~/RustroverProjects/g3)
// Previous bug: was using codepath as workspace, causing logs to go to wrong location
let workspace_path = std::path::PathBuf::from(workspace);
eprintln!("[DEBUG] Creating Project with workspace_path={}", workspace_path.display());
let project = Project::new(workspace_path.clone());
project.ensure_workspace_exists()?;
project.enter_workspace()?;
// CRITICAL: Ensure logs directory exists BEFORE creating Agent
// This guarantees <workspace>/logs/ directory is ready for log writes
project.ensure_logs_dir()?;
eprintln!("[DEBUG] Logs directory created/verified: {}", project.logs_dir().display());
// Create agent - not autonomous mode, just regular agent with tools
let mut agent = Agent::new_with_readme_and_quiet(
planner_config,
@@ -336,6 +351,9 @@ pub async fn call_refinement_llm_with_tools(
)
.await?;
eprintln!("[DEBUG] Agent created, G3_WORKSPACE_PATH should be: {}",
std::env::var("G3_WORKSPACE_PATH").unwrap_or_else(|_| "NOT SET".to_string()));
// Execute the refinement task
// The agent will have access to tools and execute them
let task = user_message;

View File

@@ -692,6 +692,7 @@ pub async fn run_coach_player_loop(
/// 4. Run the refinement and implementation loop
pub async fn run_planning_mode(
codepath: Option<String>,
workspace: Option<std::path::PathBuf>,
no_git: bool,
config_path: Option<&str>,
) -> anyhow::Result<()> {
@@ -717,16 +718,22 @@ pub async fn run_planning_mode(
anyhow::bail!("Codepath does not exist: {}", codepath.display());
}
// Set workspace path EARLY for all logging (before provider initialization)
std::env::set_var("G3_WORKSPACE_PATH", codepath.display().to_string());
// Determine workspace directory (use workspace arg if provided, else use codepath)
let workspace_dir = workspace.unwrap_or_else(|| codepath.clone());
print_msg(&format!("📁 Workspace: {}", workspace_dir.display()));
// Set G3_WORKSPACE_PATH environment variable EARLY for all logging
std::env::set_var("G3_WORKSPACE_PATH", workspace_dir.display().to_string());
eprintln!("[DEBUG] Set G3_WORKSPACE_PATH={}", workspace_dir.display());
// Create logs directory and verify it exists
let logs_dir = codepath.join("logs");
let logs_dir = workspace_dir.join("logs");
if !logs_dir.exists() {
fs::create_dir_all(&logs_dir)
.context("Failed to create logs directory")?;
}
print_msg(&format!("📁 Logs directory: {}", logs_dir.display()));
eprintln!("[DEBUG] Logs directory created/verified: {}", logs_dir.display());
// Create the LLM provider for planning
print_msg("🔧 Initializing planner provider...");
@@ -776,12 +783,17 @@ pub async fn run_planning_mode(
print_msg("\n🔄 Refinement phase - calling LLM...");
let codepath_str = config.codepath.display().to_string();
let workspace_str = workspace_dir.display().to_string();
eprintln!("[DEBUG] Calling refinement with codepath={}, workspace={}",
codepath_str, workspace_str);
// Load config and call LLM with full tool execution capability
let g3_config = g3_config::Config::load(config.config_path.as_deref())?;
let response = llm::call_refinement_llm_with_tools(
&g3_config,
&codepath_str,
&workspace_str,
).await;
match response {