codetether_agent/tool/
question.rs1use anyhow::{Context, Result};
4use async_trait::async_trait;
5use serde::Deserialize;
6use serde_json::{json, Value};
7use super::{Tool, ToolResult};
8use std::io::{self, Write};
9
10pub struct QuestionTool;
11
12impl Default for QuestionTool {
13 fn default() -> Self { Self::new() }
14}
15
16impl QuestionTool {
17 pub fn new() -> Self { Self }
18}
19
20#[derive(Deserialize)]
21struct Params {
22 question: String,
23 #[serde(default)]
24 options: Vec<String>,
25 #[serde(default)]
26 default: Option<String>,
27}
28
29#[async_trait]
30impl Tool for QuestionTool {
31 fn id(&self) -> &str { "question" }
32 fn name(&self) -> &str { "Ask Question" }
33 fn description(&self) -> &str { "Ask the user a question and wait for their response. Use for clarification or user input." }
34 fn parameters(&self) -> Value {
35 json!({
36 "type": "object",
37 "properties": {
38 "question": {"type": "string", "description": "Question to ask the user"},
39 "options": {"type": "array", "items": {"type": "string"}, "description": "Optional list of valid responses"},
40 "default": {"type": "string", "description": "Default value if user presses Enter"}
41 },
42 "required": ["question"]
43 })
44 }
45
46 async fn execute(&self, params: Value) -> Result<ToolResult> {
47 let p: Params = serde_json::from_value(params).context("Invalid params")?;
48
49 print!("\nš {}", p.question);
51
52 if !p.options.is_empty() {
53 print!(" [{}]", p.options.join("/"));
54 }
55
56 if let Some(ref default) = p.default {
57 print!(" (default: {})", default);
58 }
59
60 print!(": ");
61 io::stdout().flush()?;
62
63 let mut input = String::new();
65 io::stdin().read_line(&mut input)?;
66 let response = input.trim().to_string();
67
68 let final_response = if response.is_empty() {
69 p.default.unwrap_or_default()
70 } else {
71 response
72 };
73
74 if !p.options.is_empty() && !p.options.contains(&final_response) {
76 return Ok(ToolResult::error(format!(
77 "Invalid response '{}'. Valid options: {}",
78 final_response,
79 p.options.join(", ")
80 )));
81 }
82
83 Ok(ToolResult::success(final_response.clone())
84 .with_metadata("response", json!(final_response)))
85 }
86}