use async_trait::async_trait;
use serde_json::{Value, json};
use super::base::Tool;
use crate::mcp::registry::{ToolContext, ToolResult};
#[derive(Debug, Default)]
pub struct ExitPlanModeTool;
impl ExitPlanModeTool {
pub fn new() -> Self {
Self
}
}
#[async_trait]
impl Tool for ExitPlanModeTool {
fn name(&self) -> &str {
"ExitPlanMode"
}
fn description(&self) -> &str {
"Use this tool when you are in plan mode and have finished writing your plan to the plan file \
and are ready for user approval. This tool signals that you're done planning and ready for \
the user to review and approve. IMPORTANT: Only use this tool for tasks that require planning \
the implementation steps of code changes, not for research tasks. \
INPUT: Pass your plan content as a 'plan' parameter (e.g., {\"plan\": \"Your plan summary here\"}) \
so it can be displayed in the approval dialog."
}
fn input_schema(&self) -> Value {
json!({
"type": "object",
"description": "Exit plan mode and request user approval for the plan",
"properties": {
"plan": {
"type": "string",
"description": "The plan content that was written to the plan file"
}
},
"required": []
})
}
async fn execute(&self, _input: Value, context: &ToolContext) -> ToolResult {
tracing::info!(
"ExitPlanMode called for session {} in {}",
context.session_id,
context.cwd.display()
);
let output = "Plan mode exited. The plan is ready for user review and approval. \
Once approved, implementation can begin.";
ToolResult::success(output).with_metadata(json!({
"action": "exit_plan_mode",
"status": "awaiting_approval"
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_exit_plan_mode_properties() {
let tool = ExitPlanModeTool::new();
assert_eq!(tool.name(), "ExitPlanMode");
assert!(tool.description().contains("plan"));
assert!(tool.description().contains("approval"));
}
#[test]
fn test_exit_plan_mode_input_schema() {
let tool = ExitPlanModeTool::new();
let schema = tool.input_schema();
assert_eq!(schema["type"], "object");
assert!(schema["required"].as_array().unwrap().is_empty());
}
#[tokio::test]
async fn test_exit_plan_mode_execute() {
let temp_dir = TempDir::new().unwrap();
let tool = ExitPlanModeTool::new();
let context = ToolContext::new("test-session", temp_dir.path());
let result = tool.execute(json!({}), &context).await;
assert!(!result.is_error);
assert!(result.content.contains("Plan mode exited"));
assert!(result.content.contains("approval"));
}
#[tokio::test]
async fn test_exit_plan_mode_with_extra_params() {
let temp_dir = TempDir::new().unwrap();
let tool = ExitPlanModeTool::new();
let context = ToolContext::new("test-session", temp_dir.path());
let result = tool
.execute(json!({"extra_field": "ignored"}), &context)
.await;
assert!(!result.is_error);
}
}