Skip to main content

synaps_cli/tools/shell/
send.rs

1//! `shell_send` tool — send input to an active shell session.
2
3use serde_json::{json, Value};
4use crate::{Result, RuntimeError};
5use crate::tools::{Tool, ToolContext};
6
7pub struct ShellSendTool;
8
9#[async_trait::async_trait]
10impl Tool for ShellSendTool {
11    fn name(&self) -> &str { "shell_send" }
12
13    fn description(&self) -> &str {
14        "Send input to an active shell session. Returns the output produced after sending the input. The input is sent exactly as received after JSON parsing — a JSON string containing \\n will send an actual newline (Enter key). Use \\x03 for Ctrl-C, \\x04 for Ctrl-D."
15    }
16
17    fn parameters(&self) -> Value {
18        json!({
19            "type": "object",
20            "properties": {
21                "session_id": {
22                    "type": "string",
23                    "description": "Session ID from shell_start"
24                },
25                "input": {
26                    "type": "string",
27                    "description": "Text to send to the shell. Use \\n for Enter, \\x03 for Ctrl-C, \\x04 for Ctrl-D"
28                },
29                "timeout_ms": {
30                    "type": "integer",
31                    "description": "Override readiness timeout for this send (ms)"
32                }
33            },
34            "required": ["session_id", "input"]
35        })
36    }
37
38    async fn execute(&self, params: Value, ctx: ToolContext) -> Result<String> {
39        let mgr = ctx.capabilities.session_manager.as_ref()
40            .ok_or_else(|| RuntimeError::Tool("Shell sessions not available".into()))?;
41        
42        let session_id = params["session_id"].as_str()
43            .ok_or_else(|| RuntimeError::Tool("Missing session_id parameter".into()))?;
44        let input = params["input"].as_str()
45            .ok_or_else(|| RuntimeError::Tool("Missing input parameter".into()))?;
46        let timeout_ms = params["timeout_ms"].as_u64();
47        
48        let result = mgr.send_input(session_id, input, timeout_ms, ctx.channels.tx_delta.as_ref()).await?;
49        
50        if result.status == "active" {
51            Ok(result.output)
52        } else {
53            let mut out = result.output;
54            out.push_str(&format!("\n[{}]", result.status));
55            Ok(out)
56        }
57    }
58}