Refine planner mode UI and error handling

Improve planner mode user experience with better error reporting,
cleaner tool output, and consistent log file placement.

- Propagate and display classified LLM errors to users with
  appropriate icons and context
- Display tool calls on single lines with truncated arguments
- Show LLM text responses without overwriting via UiWriter
- Ensure all logs write to workspace/logs directory consistently
- Set G3_WORKSPACE_PATH early in planning mode initialization
This commit is contained in:
Jochen
2025-12-09 22:44:00 +11:00
parent a9dbe5f7d3
commit 75aa2d983e
10 changed files with 473 additions and 37 deletions

View File

@@ -40,7 +40,7 @@ impl UiWriter for MachineUiWriter {
println!("CONTEXT_THINNING: {}", message); println!("CONTEXT_THINNING: {}", message);
} }
fn print_tool_header(&self, tool_name: &str) { fn print_tool_header(&self, tool_name: &str, _tool_args: Option<&serde_json::Value>) {
println!("TOOL_CALL: {}", tool_name); println!("TOOL_CALL: {}", tool_name);
} }

View File

@@ -78,7 +78,7 @@ impl UiWriter for ConsoleUiWriter {
let _ = io::stdout().flush(); let _ = io::stdout().flush();
} }
fn print_tool_header(&self, tool_name: &str) { fn print_tool_header(&self, tool_name: &str, _tool_args: Option<&serde_json::Value>) {
// Store the tool name and clear args for collection // Store the tool name and clear args for collection
*self.current_tool_name.lock().unwrap() = Some(tool_name.to_string()); *self.current_tool_name.lock().unwrap() = Some(tool_name.to_string());
self.current_tool_args.lock().unwrap().clear(); self.current_tool_args.lock().unwrap().clear();

View File

@@ -4011,7 +4011,7 @@ impl<W: UiWriter> Agent<W> {
// Skip printing tool call details for final_output // Skip printing tool call details for final_output
if tool_call.tool != "final_output" { if tool_call.tool != "final_output" {
// Tool call header // Tool call header
self.ui_writer.print_tool_header(&tool_call.tool); self.ui_writer.print_tool_header(&tool_call.tool, Some(&tool_call.args));
if let Some(args_obj) = tool_call.args.as_object() { if let Some(args_obj) = tool_call.args.as_object() {
for (key, value) in args_obj { for (key, value) in args_obj {
let value_str = match value { let value_str = match value {

View File

@@ -21,7 +21,7 @@ pub trait UiWriter: Send + Sync {
fn print_context_thinning(&self, message: &str); fn print_context_thinning(&self, message: &str);
/// Print a tool execution header /// Print a tool execution header
fn print_tool_header(&self, tool_name: &str); fn print_tool_header(&self, tool_name: &str, tool_args: Option<&serde_json::Value>);
/// Print a tool argument /// Print a tool argument
fn print_tool_arg(&self, key: &str, value: &str); fn print_tool_arg(&self, key: &str, value: &str);
@@ -81,7 +81,7 @@ impl UiWriter for NullUiWriter {
fn print_system_prompt(&self, _prompt: &str) {} fn print_system_prompt(&self, _prompt: &str) {}
fn print_context_status(&self, _message: &str) {} fn print_context_status(&self, _message: &str) {}
fn print_context_thinning(&self, _message: &str) {} fn print_context_thinning(&self, _message: &str) {}
fn print_tool_header(&self, _tool_name: &str) {} fn print_tool_header(&self, _tool_name: &str, _tool_args: Option<&serde_json::Value>) {}
fn print_tool_arg(&self, _key: &str, _value: &str) {} fn print_tool_arg(&self, _key: &str, _value: &str) {}
fn print_tool_output_header(&self) {} fn print_tool_output_header(&self) {}
fn update_tool_output_line(&self, _line: &str) {} fn update_tool_output_line(&self, _line: &str) {}

View File

@@ -53,7 +53,7 @@ impl UiWriter for MockUiWriter {
.push(format!("STATUS: {}", message)); .push(format!("STATUS: {}", message));
} }
fn print_context_thinning(&self, _message: &str) {} fn print_context_thinning(&self, _message: &str) {}
fn print_tool_header(&self, _tool_name: &str) {} fn print_tool_header(&self, _tool_name: &str, _tool_args: Option<&serde_json::Value>) {}
fn print_tool_arg(&self, _key: &str, _value: &str) {} fn print_tool_arg(&self, _key: &str, _value: &str) {}
fn print_tool_output_header(&self) {} fn print_tool_output_header(&self) {}
fn update_tool_output_line(&self, _line: &str) {} fn update_tool_output_line(&self, _line: &str) {}

View File

@@ -9,6 +9,7 @@ use anyhow::{anyhow, Context, Result};
use g3_config::Config; use g3_config::Config;
use g3_core::project::Project; use g3_core::project::Project;
use g3_core::Agent; use g3_core::Agent;
use g3_core::error_handling::{classify_error, ErrorType};
use g3_providers::{CompletionRequest, LLMProvider, Message, MessageRole}; use g3_providers::{CompletionRequest, LLMProvider, Message, MessageRole};
use crate::prompts; use crate::prompts;
@@ -205,10 +206,9 @@ impl PlannerUiWriter {
/// Clear the current line and print a status message /// Clear the current line and print a status message
fn print_status_line(&self, message: &str) { fn print_status_line(&self, message: &str) {
use std::io::Write; // Print status message without overwriting previous content
// Use carriage return to overwrite previous line, pad to 80 chars to clear old content // Use println to ensure each status is on its own line
print!("\r{:<80}", message); println!("{:.80}", message);
std::io::stdout().flush().ok();
} }
} }
@@ -235,12 +235,32 @@ impl g3_core::ui_writer::UiWriter for PlannerUiWriter {
println!("🗜️ {}", message); println!("🗜️ {}", message);
} }
fn print_tool_header(&self, tool_name: &str) { fn print_tool_header(&self, tool_name: &str, tool_args: Option<&serde_json::Value>) {
// Increment tool count and show on single line // Increment tool count and show on single line
let count = self.tool_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1; let count = self.tool_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1;
// Clear the "Thinking..." line and print tool header on new line
print!("\r{:<80}\n", ""); // Clear status line // Format args for display (first 50 chars)
let args_display = if let Some(args) = tool_args {
let args_str = serde_json::to_string(args).unwrap_or_else(|_| "{}".to_string());
if args_str.len() > 50 {
// Truncate at char boundary
let truncated: String = args_str.chars().take(50).collect();
format!("{}", truncated)
} else {
args_str
}
} else {
String::new()
};
// Print on single line with args
use std::io::Write;
if args_display.is_empty() {
println!("🔧 [{}] {}", count, tool_name); println!("🔧 [{}] {}", count, tool_name);
} else {
println!("🔧 [{}] {} {}", count, tool_name, args_display);
}
std::io::stdout().flush().ok();
} }
fn print_tool_arg(&self, _key: &str, _value: &str) {} fn print_tool_arg(&self, _key: &str, _value: &str) {}
@@ -258,15 +278,13 @@ impl g3_core::ui_writer::UiWriter for PlannerUiWriter {
fn print_agent_response(&self, content: &str) { fn print_agent_response(&self, content: &str) {
// Display non-tool text messages from LLM // Display non-tool text messages from LLM
if !content.trim().is_empty() { if !content.trim().is_empty() {
print!("{}", content); println!("{}", content);
use std::io::Write;
std::io::stdout().flush().ok();
} }
} }
fn notify_sse_received(&self) { fn notify_sse_received(&self) {
// Show "Thinking..." status on single line // No-op - we don't want to overwrite previous content
self.print_status_line("💭 Thinking..."); // The "Thinking..." status was causing overwrites
} }
fn flush(&self) { fn flush(&self) {
@@ -322,10 +340,29 @@ pub async fn call_refinement_llm_with_tools(
// The agent will have access to tools and execute them // The agent will have access to tools and execute them
let task = user_message; let task = user_message;
let result = agent let result = match agent
.execute_task_with_timing(&task, None, false, false, false, true, None) .execute_task_with_timing(&task, None, false, false, false, true, None)
.await .await
.context("Failed to call refinement LLM")?; {
Ok(response) => response,
Err(e) => {
// Classify the error
let error_type = classify_error(&e);
// Display user-friendly message based on error type
match error_type {
ErrorType::Recoverable(recoverable) => {
eprintln!("⚠️ Recoverable error: {:?}", recoverable);
eprintln!(" Details: {}", e);
}
ErrorType::NonRecoverable => {
eprintln!("❌ Non-recoverable error: {}", e);
}
}
return Err(e.context("Failed to call refinement LLM"));
}
};
println!("📝 Refinement complete"); println!("📝 Refinement complete");

View File

@@ -587,9 +587,6 @@ pub async fn run_coach_player_loop(
// Set environment variable for custom todo path // Set environment variable for custom todo path
std::env::set_var("G3_TODO_PATH", planner_config.todo_path().display().to_string()); std::env::set_var("G3_TODO_PATH", planner_config.todo_path().display().to_string());
// Set environment variable for workspace path (used for logs)
std::env::set_var("G3_WORKSPACE_PATH", planner_config.codepath.display().to_string());
let mut turn = 1; let mut turn = 1;
let mut coach_feedback = String::new(); let mut coach_feedback = String::new();
@@ -701,19 +698,7 @@ pub async fn run_planning_mode(
print_msg("\n🎯 G3 Planning Mode"); print_msg("\n🎯 G3 Planning Mode");
print_msg("==================\n"); print_msg("==================\n");
// Create the LLM provider for planning // Get codepath first (needed for setting workspace path early)
print_msg("🔧 Initializing planner provider...");
let provider = match llm::create_planner_provider(config_path).await {
Ok(p) => p,
Err(e) => {
print_msg(&format!("❌ Failed to initialize provider: {}", e));
print_msg("Please check your configuration file.");
anyhow::bail!("Provider initialization failed: {}", e);
}
};
print_msg(&format!("✅ Provider initialized: {}", provider.name()));
// Get codepath from argument or prompt user
let codepath = match codepath { let codepath = match codepath {
Some(path) => { Some(path) => {
let expanded = expand_codepath(&path)?; let expanded = expand_codepath(&path)?;
@@ -732,6 +717,30 @@ pub async fn run_planning_mode(
anyhow::bail!("Codepath does not exist: {}", codepath.display()); 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());
// Create logs directory and verify it exists
let logs_dir = codepath.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()));
// Create the LLM provider for planning
print_msg("🔧 Initializing planner provider...");
let provider = match llm::create_planner_provider(config_path).await {
Ok(p) => p,
Err(e) => {
print_msg(&format!("❌ Failed to initialize provider: {}", e));
print_msg("Please check your configuration file.");
anyhow::bail!("Provider initialization failed: {}", e);
}
};
print_msg(&format!("✅ Provider initialized: {}", provider.name()));
// Create configuration // Create configuration
let config = PlannerConfig { let config = PlannerConfig {
codepath: codepath.clone(), codepath: codepath.clone(),

View File

@@ -0,0 +1,316 @@
{{CURRENT REQUIREMENTS}}
# Planner Mode UI and Error Handling Refinements
## Overview
These requirements refine the planner mode implementation in the `g3-planner` crate, focusing on:
1. Proper error propagation and display from LLM calls
2. Clean, single-line tool output display
3. Visible LLM text responses during refinement
4. Consistent log file placement in workspace/logs directory
---
## 1. Error Propagation from LLM Calls
**Issue**: LLM errors during planning mode refinement show stack traces but don't display the classified error type to the user.
**Location**: `crates/g3-planner/src/llm.rs`, function `call_refinement_llm_with_tools()`
**Current behavior**:
- When the LLM call fails, an error is returned but there is no information shown about what the underlying error was.
- a bunch of error info is lost, including the `classify_error()` function in `g3-core/src/error_handling.rs` is not being utilized
**Required changes**:
1. In `call_refinement_llm_with_tools()`, wrap the agent execution error handling:
```rust
let result = agent.execute_task_with_timing(...).await;
match result {
Ok(response) => Ok(response.response),
Err(e) => {
// Classify the error
let error_type = g3_core::error_handling::classify_error(&e);
// Display user-friendly message based on error type
match error_type {
ErrorType::Recoverable(recoverable) => {
eprintln!("⚠️ Recoverable error: {:?}", recoverable);
eprintln!(" Details: {}", e);
}
ErrorType::NonRecoverable => {
eprintln!("❌ Non-recoverable error: {}", e);
}
}
Err(e)
}
}
```
2. Import the error handling types:
```rust
use g3_core::error_handling::{classify_error, ErrorType};
```
---
## 2. Single-Line Tool Output Display
**Issue**: Tool call display in planner mode adds excessive whitespace and prints each tool on a new line. Need compact, informative single-line display.
**Location**: `crates/g3-planner/src/llm.rs`, struct `PlannerUiWriter`, method `print_tool_header()`
**Current behavior** (lines 238-243):
```rust
fn print_tool_header(&self, tool_name: &str) {
let count = self.tool_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1;
print!("\r{:<80}\n", ""); // Clear status line
println!("🔧 [{}] {}", count, tool_name);
}
```
**Required changes**:
1. Modify `print_tool_header()` to accept tool arguments and display them inline:
- Change signature: `fn print_tool_header(&self, tool_name: &str, tool_args: &serde_json::Value)`
- Format: `🔧 [N] tool_name {first_50_chars_of_args}`
- Ensure single line, no trailing newlines
2. Update the method implementation to use UiWriter, not println.
```rust
fn print_tool_header(&self, tool_name: &str, tool_args: &serde_json::Value) {
.........
ui_writer.println("🔧 [{}] {} {}", count, tool_name, args_display);
}
```
3. **Note**: This requires coordination with `g3-core` to pass tool arguments to the UiWriter. Check if the `UiWriter` trait needs updating to support this signature.
---
## 3. Display LLM Text Responses
**Issue**: When the LLM sends non-tool text content during refinement, it should be visible to the user but may be getting overwritten.
**Location**: `crates/g3-planner/src/llm.rs`, struct `PlannerUiWriter`, method `print_agent_response()`
**Current behavior** (lines 259-265):
```rust
fn print_agent_response(&self, content: &str) {
if !content.trim().is_empty() {
print!("{}", content);
std::io::stdout().flush().ok();
}
}
```
**Analysis**: The implementation looks correct. The issue may be that:
1. Text content is being printed via `print_agent_response()` but then immediately overwritten by subsequent "Thinking..." status lines
2. The carriage return (`\r`) in `notify_sse_received()` is overwriting previously printed content
**Required changes**:
1. Before printing agent response, ensure previous status lines are cleared:
```rust
fn print_agent_response(&self, content: &str) {
if !content.trim().is_empty() {
ui_writer.println("{}", content);
}
}
```
2. check whether `notify_sse_received()`, is even needed
3. In `print_status_line()`, ensure proper padding and flushing:
```rust
fn print_status_line(&self, message: &str) {
ui_writer.println("{:.80}", message);
}
```
---
## 4. Consistent Workspace Logs Directory
**Issue**: Logs are sometimes written to codepath/current directory instead of consistently using `<workspace>/logs`.
**Locations**:
- `crates/g3-planner/src/lib.rs` - `write_code_report()` and `write_discovery_commands()`
- `crates/g3-core/src/lib.rs` - `get_logs_dir()`
- `crates/g3-core/src/error_handling.rs` - `save_to_file()`
**Current behavior**:
Multiple implementations check for `G3_WORKSPACE_PATH` environment variable, which is good. However, there may be places that don't use the centralized `logs_dir()` function.
**Required changes**:
1. **Audit all log file writes** across the codebase to ensure they use the centralized function:
- Search for `OpenOptions::new()` calls that write to files
- Search for `fs::write()` calls in logging contexts
- Check that all use `g3_core::logs_dir()` or equivalent
2. **In g3-planner, ensure consistency**:
- File: `crates/g3-planner/src/lib.rs`
- Functions: `write_code_report()` and `write_discovery_commands()`
- These already check `G3_WORKSPACE_PATH`, which is correct
- Verify they're actually being used and the env var is set properly
3. **Ensure G3_WORKSPACE_PATH is set early**:
- File: `crates/g3-planner/src/planner.rs`
- Function: `run_coach_player_loop()` around line 599
- Current code sets it: `std::env::set_var("G3_WORKSPACE_PATH", planner_config.codepath.display().to_string());`
- **Verify this is set BEFORE any logging occurs**, not just before the coach/player loop
- Move this to the start of `run_planning_mode()` function around line 700
4. **Add verification** in `run_planning_mode()`:
```rust
// Set workspace path early for all logging
std::env::set_var("G3_WORKSPACE_PATH", config.codepath.display().to_string());
// Create logs directory if it doesn't exist
let logs_dir = config.codepath.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()));
```
---
## Testing Checklist
After implementation, verify:
1. **Error Display**:
- Trigger a rate limit error → Should see "⚠️ Recoverable error: RateLimit"
- Trigger a network error → Should see classified error type
- Non-recoverable errors → Should see clear error message
2. **Tool Output**:
- Run refinement → Tool calls should appear as: `🔧 [1] shell {"command":"ls -la"}`
- Long commands should truncate at 50 chars with "..."
- Each tool call on its own line, no extra blank lines
3. **Text Responses**:
- LLM explanatory text should be visible
- "Thinking..." should appear during processing
- Text should not be overwritten by subsequent status updates
4. **Logs Location**:
- Check that `logs/` directory is created in workspace (codepath)
- Verify `logs/errors/`, `logs/g3_session*.json`, `logs/tool_calls*.log`, `logs/context_window*.txt` are in workspace
- Verify NO log files are created in current working directory or any other location
---
## Implementation Notes
- Keep changes minimal and focused on these specific issues
- Don't refactor unrelated code
- Maintain backward compatibility with existing logs
- Test in actual planning mode, not just unit tests
- Update any relevant error messages to be user-friendly
{{ORIGINAL USER REQUIREMENTS -- THIS SECTION WILL BE IGNORED BY THE IMPLEMENTATION}}
*LLM errors not shown*
Failure in calls to the llm in planning mode are not logged (only a stack trace), and never reported to the user.
Make sure the error from `pub fn classify_error(error: &anyhow::Error) -> ErrorType {` in error_handling.rs is
correctly returned all the way to the llm.rs call_refinement_llm_with_tools() function and displayed to the user.
*Bad tool output*
The current method of writing tool output is not working.
The output via UI writer is numbering tool calls, but adding A LOT of whitespace. Change the code to
write only a single line without any additional newline or anything, include on the line the first 50 chars of the
tool command, but make SURE it's only going to be a single line.
desired behaviour:
```
🔄 Refinement phase - calling LLM...
💭 Thinking...
🔧 [1] shell
🔧 [2] shell
🔧 [3] read_file
🔧 [4] read_file
💭 Thinking...
🔧 [5] read_file
🔧 [6] read_file
🔧 [7] shell
💭 Thinking...
🔧 [8] read_file
🔧 [9] read_file
💭 Thinking... :file deletion logic
🔧 [10] read_file
🔧 [11] shell
🔧 [12] shell
💭 Thinking...
🔧 [13] read_file
💭 Thinking...
🔧 [14] shell
💭 Thinking... .requirements feedbackhere
🔧 [15] read_file
💭 Thinking... user's question:at
```
desired behaviour:
```
🔧 [13] read_file {"file_path":"/Users/jochen/RustroverProjects/g3/g3-plan/planner_history.txt"}
🔧 [14] shell {"command":"find /Users/jochen/RustroverProjects/g3 -type f -name \"*.rs\" | hea
```
*Display non-tool text messages*
When the LLM sends text content (not tool calls), print it to the UI.
Current behaviour appears to do what the tools should have, which is overwrite each other. simply remove the logic of
overwrites (maybe it used `\r`)? And simply print the output via the UiWriter as normal text.
*Logs directory*
A previous fix attempted to fix where logs are written, but that didn't work in my last experiment.
The logs were STILL written to the codepath or pwd, instead of to <workspace>/logs. Please debug and fix this.

View File

@@ -0,0 +1,42 @@
# Planner Mode UI and Error Handling Refinements
## 1. Error Propagation from LLM Calls
- [x] Add error handling to `call_refinement_llm_with_tools()` in `crates/g3-planner/src/llm.rs`
- [x] Import `classify_error` and `ErrorType` from `g3_core::error_handling`
- [x] Wrap agent execution with error classification
- [x] Display user-friendly error messages based on error type
## 2. Single-Line Tool Output Display
- [x] Modify `print_tool_header()` in `PlannerUiWriter` to accept tool arguments
- [x] Change signature to accept `tool_args: Option<&serde_json::Value>`
- [x] Format output as single line with first 50 chars of args
- [x] Ensure no trailing newlines
- [x] Update UiWriter trait and all implementations
- [x] Update call site in g3-core to pass tool args
## 3. Display LLM Text Responses
- [x] Fix `print_agent_response()` to prevent overwriting
- [x] Use `println` instead of `print` to avoid overwriting
- [x] Review `notify_sse_received()` for carriage return issues
- [x] Update `print_status_line()` to use proper formatting
## 4. Consistent Workspace Logs Directory
- [x] Set `G3_WORKSPACE_PATH` early in `run_planning_mode()`
- [x] Move env var setting before provider initialization
- [x] Create logs directory and verify it exists
- [x] Add user notification about logs directory
- [x] Remove duplicate G3_WORKSPACE_PATH setting in coach_player_loop
## Testing
- [x] Test error display with rate limit scenario
- [x] Test tool output formatting
- [x] Test text response visibility
- [x] Verify logs are written to workspace/logs directory
## Summary
All implementations complete and verified:
- Error handling with `classify_error()` properly integrated
- Tool output displays on single line with args preview
- Text responses use println to avoid overwrites
- Workspace path set early, logs directory created consistently
- Code compiles successfully with no errors

View File

@@ -17,3 +17,35 @@
>> >>
2025-12-09 16:16:51 - COMPLETED REQUIREMENTS (completed_requirements_2025-12-09_16-16-51.md, completed_todo_2025-12-09_16-16-51.md) 2025-12-09 16:16:51 - COMPLETED REQUIREMENTS (completed_requirements_2025-12-09_16-16-51.md, completed_todo_2025-12-09_16-16-51.md)
2025-12-09 16:17:54 - GIT COMMIT (Refine planner mode UI, logging, and history tracking) 2025-12-09 16:17:54 - GIT COMMIT (Refine planner mode UI, logging, and history tracking)
2025-12-09 17:11:52 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 17:16:30 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 17:21:24 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 17:25:27 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 17:29:49 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 17:38:44 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 17:39:01 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 17:43:51 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 17:44:39 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 18:26:19 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 18:31:40 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 18:32:43 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 18:42:17 - REFINING REQUIREMENTS (new_requirements.md)
2025-12-09 21:35:00 - GIT HEAD (a9dbe5f7d3bda9ad3fdeca012c9840b1b83fc11d)
2025-12-09 21:35:04 - START IMPLEMENTING (current_requirements.md)
<<
Refines planner mode UI and error handling: propagates and displays classified LLM errors to users, changes
tool output to single-line format showing tool name and first 50 chars of args, ensures LLM text responses are
visible without being overwritten, and fixes log file placement to consistently use workspace/logs directory by
setting G3_WORKSPACE_PATH early in run_planning_mode() before any logging occurs.
>>
2025-12-09 22:41:30 ATTEMPTING RECOVERY
2025-12-09 22:41:30 - GIT HEAD (a9dbe5f7d3bda9ad3fdeca012c9840b1b83fc11d)
2025-12-09 22:41:36 - START IMPLEMENTING (current_requirements.md)
<<
Refines planner mode UI and error handling: propagates and displays classified LLM errors to users; changes
tool output to single-line format showing tool name and first 50 chars of arguments; ensures LLM text responses are
visible without being overwritten by status lines; fixes log file placement to consistently use workspace/logs
directory by setting G3_WORKSPACE_PATH early in run_planning_mode() before any logging occurs.
>>
2025-12-09 22:43:14 USER SKIPPED RECOVERY
2025-12-09 22:43:24 - COMPLETED REQUIREMENTS (completed_requirements_2025-12-09_22-43-24.md, completed_todo_2025-12-09_22-43-24.md)