Skip to main content

cersei_tools/
plan_mode.rs

1//! Plan mode tools: enter/exit read-only planning mode.
2
3use super::*;
4use std::sync::atomic::{AtomicBool, Ordering};
5
6/// Global plan mode flag, shared across tool invocations.
7static PLAN_MODE: AtomicBool = AtomicBool::new(false);
8
9/// Check if plan mode is currently active.
10pub fn is_plan_mode() -> bool {
11    PLAN_MODE.load(Ordering::Relaxed)
12}
13
14/// Set plan mode state programmatically.
15pub fn set_plan_mode(active: bool) {
16    PLAN_MODE.store(active, Ordering::Relaxed);
17}
18
19pub struct EnterPlanModeTool;
20
21#[async_trait]
22impl Tool for EnterPlanModeTool {
23    fn name(&self) -> &str {
24        "EnterPlanMode"
25    }
26    fn description(&self) -> &str {
27        "Enter plan mode: restricts to read-only tools for safe exploration and planning."
28    }
29    fn permission_level(&self) -> PermissionLevel {
30        PermissionLevel::None
31    }
32
33    fn input_schema(&self) -> Value {
34        serde_json::json!({
35            "type": "object",
36            "properties": {},
37            "required": []
38        })
39    }
40
41    async fn execute(&self, _input: Value, _ctx: &ToolContext) -> ToolResult {
42        PLAN_MODE.store(true, Ordering::Relaxed);
43        ToolResult::success("Plan mode activated. Only read-only tools are available.")
44    }
45}
46
47pub struct ExitPlanModeTool;
48
49#[async_trait]
50impl Tool for ExitPlanModeTool {
51    fn name(&self) -> &str {
52        "ExitPlanMode"
53    }
54    fn description(&self) -> &str {
55        "Exit plan mode and return to full tool access."
56    }
57    fn permission_level(&self) -> PermissionLevel {
58        PermissionLevel::None
59    }
60
61    fn input_schema(&self) -> Value {
62        serde_json::json!({
63            "type": "object",
64            "properties": {},
65            "required": []
66        })
67    }
68
69    async fn execute(&self, _input: Value, _ctx: &ToolContext) -> ToolResult {
70        PLAN_MODE.store(false, Ordering::Relaxed);
71        ToolResult::success("Plan mode deactivated. Full tool access restored.")
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use crate::permissions::AllowAll;
79    use std::sync::Arc;
80
81    fn test_ctx() -> ToolContext {
82        ToolContext {
83            working_dir: std::env::temp_dir(),
84            session_id: "test".into(),
85            permissions: Arc::new(AllowAll),
86            cost_tracker: Arc::new(CostTracker::new()),
87            mcp_manager: None,
88            extensions: Extensions::default(),
89        }
90    }
91
92    #[tokio::test]
93    async fn test_plan_mode_toggle() {
94        set_plan_mode(false);
95        assert!(!is_plan_mode());
96
97        let enter = EnterPlanModeTool;
98        enter.execute(serde_json::json!({}), &test_ctx()).await;
99        assert!(is_plan_mode());
100
101        let exit = ExitPlanModeTool;
102        exit.execute(serde_json::json!({}), &test_ctx()).await;
103        assert!(!is_plan_mode());
104    }
105}