Skip to main content

codetether_agent/tool/
question.rs

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