sgr_agent/agents/
clarification.rs1use crate::agent_tool::{Tool, ToolError, ToolOutput};
7use crate::context::AgentContext;
8use serde_json::Value;
9
10pub struct ClarificationTool;
16
17#[async_trait::async_trait]
18impl Tool for ClarificationTool {
19 fn name(&self) -> &str {
20 "ask_user"
21 }
22 fn description(&self) -> &str {
23 "Ask the user a clarifying question when you need more information to proceed"
24 }
25 fn is_system(&self) -> bool {
26 true
27 }
28 fn parameters_schema(&self) -> Value {
29 serde_json::json!({
30 "type": "object",
31 "properties": {
32 "question": {
33 "type": "string",
34 "description": "The question to ask the user"
35 }
36 },
37 "required": ["question"]
38 })
39 }
40 async fn execute(&self, args: Value, _ctx: &mut AgentContext) -> Result<ToolOutput, ToolError> {
41 let question = args
42 .get("question")
43 .and_then(|v| v.as_str())
44 .unwrap_or("Could you provide more details?");
45 Ok(ToolOutput::waiting(question))
46 }
47}
48
49pub struct PlanTool;
65
66#[async_trait::async_trait]
67impl Tool for PlanTool {
68 fn name(&self) -> &str {
69 "submit_plan"
70 }
71 fn description(&self) -> &str {
72 "Submit your implementation plan after analyzing the codebase. Call when ready to present the plan."
73 }
74 fn is_system(&self) -> bool {
75 true
76 }
77 fn parameters_schema(&self) -> Value {
78 serde_json::json!({
79 "type": "object",
80 "properties": {
81 "summary": {
82 "type": "string",
83 "description": "Brief summary of what the plan achieves"
84 },
85 "steps": {
86 "type": "array",
87 "description": "Ordered list of implementation steps",
88 "items": {
89 "type": "object",
90 "properties": {
91 "description": {
92 "type": "string",
93 "description": "What this step does"
94 },
95 "files": {
96 "type": "array",
97 "items": { "type": "string" },
98 "description": "Files to create or modify"
99 },
100 "tool_hints": {
101 "type": "array",
102 "items": { "type": "string" },
103 "description": "Tools likely needed for this step"
104 }
105 },
106 "required": ["description"]
107 }
108 }
109 },
110 "required": ["summary", "steps"]
111 })
112 }
113 async fn execute(&self, args: Value, ctx: &mut AgentContext) -> Result<ToolOutput, ToolError> {
114 let summary = args
115 .get("summary")
116 .and_then(|v| v.as_str())
117 .unwrap_or("Plan submitted")
118 .to_string();
119 ctx.set("plan", args);
121 Ok(ToolOutput::done(format!("Plan submitted: {summary}")))
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[tokio::test]
130 async fn clarification_returns_waiting() {
131 let tool = ClarificationTool;
132 let mut ctx = AgentContext::new();
133 let args = serde_json::json!({"question": "Which database?"});
134 let output = tool.execute(args, &mut ctx).await.unwrap();
135 assert!(output.waiting);
136 assert!(!output.done);
137 assert_eq!(output.content, "Which database?");
138 }
139
140 #[tokio::test]
141 async fn clarification_default_question() {
142 let tool = ClarificationTool;
143 let mut ctx = AgentContext::new();
144 let output = tool.execute(serde_json::json!({}), &mut ctx).await.unwrap();
145 assert!(output.waiting);
146 assert!(output.content.contains("more details"));
147 }
148
149 #[tokio::test]
150 async fn plan_tool_stores_and_completes() {
151 let tool = PlanTool;
152 let mut ctx = AgentContext::new();
153 let args = serde_json::json!({
154 "summary": "Add auth",
155 "steps": [
156 {"description": "Create module", "files": ["src/auth.rs"]},
157 {"description": "Add tests"}
158 ]
159 });
160 let output = tool.execute(args, &mut ctx).await.unwrap();
161 assert!(output.done);
162 assert!(output.content.contains("Add auth"));
163
164 let plan = ctx.get("plan").unwrap();
166 assert_eq!(plan["steps"].as_array().unwrap().len(), 2);
167 }
168
169 #[test]
170 fn clarification_is_system_tool() {
171 assert!(ClarificationTool.is_system());
172 }
173
174 #[test]
175 fn plan_is_system_tool() {
176 assert!(PlanTool.is_system());
177 }
178}