matrixcode_core/tools/
plan_mode.rs1use anyhow::Result;
2use async_trait::async_trait;
3use serde_json::{Value, json};
4use std::sync::Arc;
5use tokio::sync::Mutex;
6
7use super::{Tool, ToolDefinition};
8use crate::approval::RiskLevel;
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum PlanState {
13 None,
14 Active,
15 Committed,
16}
17
18#[derive(Debug, Clone)]
20pub struct PlanInfo {
21 pub state: PlanState,
22 pub plan_content: String,
23 pub files_to_modify: Vec<String>,
24 pub created_at: Option<std::time::Instant>,
25}
26
27static PLAN_STATE: std::sync::OnceLock<Arc<Mutex<PlanInfo>>> = std::sync::OnceLock::new();
28
29fn get_plan_state() -> Arc<Mutex<PlanInfo>> {
30 PLAN_STATE
31 .get_or_init(|| {
32 Arc::new(Mutex::new(PlanInfo {
33 state: PlanState::None,
34 plan_content: String::new(),
35 files_to_modify: Vec::new(),
36 created_at: None,
37 }))
38 })
39 .clone()
40}
41
42pub struct EnterPlanModeTool;
44
45#[async_trait]
46impl Tool for EnterPlanModeTool {
47 fn definition(&self) -> ToolDefinition {
48 ToolDefinition {
49 name: "enter_plan_mode".to_string(),
50 description: "进入规划模式,在执行前设计实现方案。适用于:(1) 需规划的非 trivial 实现任务;(2) 可受益于架构考量的问题;(3) 可能产生重大影响的变更。返回分步计划并识别关键文件。".to_string(),
51 parameters: json!({
52 "type": "object",
53 "properties": {}
54 }),
55 ..Default::default()
56 }
57 }
58
59 fn risk_level(&self) -> RiskLevel {
60 RiskLevel::Safe }
62
63 async fn execute(&self, _params: Value) -> Result<String> {
64 let plan = get_plan_state();
65 let mut state = plan.lock().await;
66
67 if state.state == PlanState::Active {
68 return Ok(
69 "Already in plan mode. Continue planning or use exit_plan_mode to finish."
70 .to_string(),
71 );
72 }
73
74 state.state = PlanState::Active;
75 state.plan_content = String::new();
76 state.files_to_modify = Vec::new();
77 state.created_at = Some(std::time::Instant::now());
78
79 Ok("Entered plan mode. Design your implementation approach:\n\n1. Analyze the task requirements\n2. Identify key files and components\n3. Consider architectural trade-offs\n4. Create step-by-step implementation plan\n5. Use exit_plan_mode to commit and execute\n\nNote: In plan mode, focus on analysis and design. Tool executions will be limited to read-only operations.".to_string())
80 }
81}
82
83pub struct ExitPlanModeTool;
85
86#[async_trait]
87impl Tool for ExitPlanModeTool {
88 fn definition(&self) -> ToolDefinition {
89 ToolDefinition {
90 name: "exit_plan_mode".to_string(),
91 description: "退出规划模式。若计划被批准,代理将执行计划的变更;若被拒绝,计划将被丢弃且不做任何修改。".to_string(),
92 parameters: json!({
93 "type": "object",
94 "properties": {
95 "plan": {
96 "type": "string",
97 "description": "要提交的实现计划(可选,若已记录)"
98 },
99 "files_to_modify": {
100 "type": "array",
101 "items": {"type": "string"},
102 "description": "将要修改的文件列表(可选)"
103 },
104 "approved": {
105 "type": "boolean",
106 "default": true,
107 "description": "是否批准执行计划"
108 }
109 }
110 }),
111 ..Default::default()
112 }
113 }
114
115 fn risk_level(&self) -> RiskLevel {
116 RiskLevel::Mutating
117 }
118
119 async fn execute(&self, params: Value) -> Result<String> {
120 let plan_content = params["plan"].as_str();
121 let files_to_modify = params["files_to_modify"].as_array().map(|arr| {
122 arr.iter()
123 .filter_map(|v| v.as_str().map(|s| s.to_string()))
124 .collect::<Vec<_>>()
125 });
126 let approved = params["approved"].as_bool().unwrap_or(true);
127
128 let plan = get_plan_state();
129 let mut state = plan.lock().await;
130
131 if state.state != PlanState::Active {
132 return Ok("Not in plan mode. Use enter_plan_mode first.".to_string());
133 }
134
135 if let Some(content) = plan_content {
137 state.plan_content = content.to_string();
138 }
139 if let Some(files) = files_to_modify {
140 state.files_to_modify = files;
141 }
142
143 if approved {
144 state.state = PlanState::Committed;
145
146 let files_str = if state.files_to_modify.is_empty() {
147 "No specific files identified".to_string()
148 } else {
149 state.files_to_modify.join(", ")
150 };
151
152 Ok(format!(
153 "Plan committed. Ready to execute.\n\nPlan: {}\nFiles to modify: {}\n\nNow proceeding with implementation...",
154 state.plan_content, files_str
155 ))
156 } else {
157 state.state = PlanState::None;
158 state.plan_content.clear();
159 state.files_to_modify.clear();
160
161 Ok(
162 "Plan rejected and discarded. Returning to normal mode without making changes."
163 .to_string(),
164 )
165 }
166 }
167}
168
169pub fn is_in_plan_mode() -> bool {
171 false }
174
175pub async fn get_current_plan_state() -> PlanState {
177 let plan = get_plan_state();
178 let state = plan.lock().await;
179 state.state.clone()
180}