Exit plan mode when plan is completed or blocked

When a plan reaches a terminal state (all items done or blocked) in
interactive mode, automatically exit plan mode and return to normal
prompt.

Changes:
- Add Agent::is_plan_terminal() method to check if plan is complete
- Add check_and_exit_plan_mode_if_terminal() helper in interactive.rs
- Call the helper after each execute_user_input() to detect completion

Fixes issue where plan mode prompt ' >> ' persisted after plan completion.
This commit is contained in:
Dhanji R. Prasanna
2026-02-05 20:31:24 +11:00
parent 30627bce97
commit 19162b1fe6
2 changed files with 39 additions and 1 deletions

View File

@@ -143,6 +143,22 @@ async fn execute_user_input<W: UiWriter>(
}
}
/// Check if plan is terminal and exit plan mode if so.
///
/// Returns true if plan mode was exited (plan is complete or all blocked).
fn check_and_exit_plan_mode_if_terminal<W: UiWriter>(
agent: &mut Agent<W>,
in_plan_mode: &mut bool,
output: &SimpleOutput,
) -> bool {
if *in_plan_mode && agent.is_plan_terminal() {
output.print("\n📋 Plan complete - exiting plan mode");
*in_plan_mode = false;
agent.set_plan_mode(false);
return true;
}
false
}
/// Run interactive mode with console output.
/// If `agent_name` is Some, we're in agent+chat mode: skip session resume/verbose welcome,
@@ -298,6 +314,9 @@ pub async fn run_interactive<W: UiWriter>(
execute_user_input(
&mut agent, &final_input, show_prompt, show_code, &output, from_agent_mode
).await;
// Check if plan completed and exit plan mode if so
check_and_exit_plan_mode_if_terminal(&mut agent, &mut in_plan_mode, &output);
} else {
// Single line input
let input = line.trim().to_string();
@@ -365,6 +384,9 @@ pub async fn run_interactive<W: UiWriter>(
execute_user_input(
&mut agent, &final_input, show_prompt, show_code, &output, from_agent_mode
).await;
// Check if plan completed and exit plan mode if so
check_and_exit_plan_mode_if_terminal(&mut agent, &mut in_plan_mode, &output);
}
}
Err(ReadlineError::Interrupted) => {

View File

@@ -50,7 +50,7 @@ pub use skills::{Skill, discover_skills, generate_skills_prompt};
#[cfg(test)]
mod task_result_comprehensive_tests;
use crate::ui_writer::UiWriter;
use tools::plan::{check_plan_approval_gate, ApprovalGateResult};
use tools::plan::{check_plan_approval_gate, read_plan, ApprovalGateResult};
#[cfg(test)]
mod tilde_expansion_tests;
@@ -1548,6 +1548,22 @@ impl<W: UiWriter> Agent<W> {
self.in_plan_mode
}
/// Check if the current plan is in a terminal state (all items done or blocked).
///
/// Returns true if:
/// - A plan exists AND all items are in terminal state (done or blocked)
///
/// Returns false if:
/// - No session_id is set
/// - No plan exists for the session
/// - Plan has items that are not terminal (todo or doing)
pub fn is_plan_terminal(&self) -> bool {
let Some(session_id) = &self.session_id else {
return false;
};
read_plan(session_id).ok().flatten().map_or(false, |plan| plan.is_complete())
}
// =========================================================================
// STREAMING & LLM INTERACTION
// =========================================================================