feat: add compact UI output for Plan Mode tools

Plan tools (plan_read, plan_write) now display with elegant tree-style
formatting similar to the old todo_write UI:

- State indicators: □ (todo), ◐ (doing), ■ (done), ⊘ (blocked)
- Tree prefixes (├/└) for items with child details
- Strikethrough for completed items
- Shows touches and all three checks (happy/negative/boundary)
- Displays plan file path link at the end

plan_approve uses compact single-line format like read_file:
- Shows approval status and revision number
- Handles already-approved and error cases

Changes:
- Add print_plan_compact() to UiWriter trait with default impl
- Implement print_plan_compact() in ConsoleUiWriter
- Call print_plan_compact() from execute_plan_read/write
- Add plan_read/plan_write to is_self_handled_tool()
- Add plan_approve to is_compact_tool() with format_plan_approve_summary()
- Add serde_yaml dependency to g3-cli
This commit is contained in:
Dhanji R. Prasanna
2026-02-02 15:30:05 +11:00
parent d6b7177107
commit 571188305a
6 changed files with 167 additions and 2 deletions

View File

@@ -758,16 +758,23 @@ pub async fn execute_plan_read<W: UiWriter>(
None => return Ok("❌ No active session - plans are session-scoped.".to_string()),
};
let plan_path = get_plan_path(session_id);
let plan_path_str = plan_path.to_string_lossy().to_string();
match read_plan(session_id)? {
Some(plan) => {
let yaml = serde_yaml::to_string(&plan)?;
ctx.ui_writer.print_plan_compact(Some(&yaml), Some(&plan_path_str), false);
Ok(format!(
"📋 {}\n\n```yaml\n{}```",
plan.status_summary(),
yaml
))
}
None => Ok("📋 No plan exists for this session. Use plan_write to create one.".to_string()),
None => {
ctx.ui_writer.print_plan_compact(None, None, false);
Ok("📋 No plan exists for this session. Use plan_write to create one.".to_string())
}
}
}
@@ -826,6 +833,12 @@ pub async fn execute_plan_write<W: UiWriter>(
return Ok(format!("❌ Failed to write plan: {}", e));
}
// Display the plan in compact format
let plan_path = get_plan_path(session_id);
let plan_path_str = plan_path.to_string_lossy().to_string();
let yaml = serde_yaml::to_string(&plan)?;
ctx.ui_writer.print_plan_compact(Some(&yaml), Some(&plan_path_str), true);
// Check if plan is now complete and trigger verification
if plan.is_complete() && plan.is_approved() {
let verification = plan_verify(&plan, ctx.working_dir);