claude_rust_tools/infrastructure/
powershell_tool.rs1use claude_rust_errors::AppResult;
2use claude_rust_types::{InterruptBehavior, PermissionLevel, Tool};
3use serde_json::{Value, json};
4use tokio::process::Command;
5
6pub struct PowerShellTool;
8
9impl PowerShellTool {
10 pub fn new() -> Self {
11 Self
12 }
13}
14
15#[async_trait::async_trait]
16impl Tool for PowerShellTool {
17 fn name(&self) -> &str {
18 "powershell"
19 }
20
21 fn description(&self) -> &str {
22 "Execute a PowerShell command and return its output."
23 }
24
25 fn input_schema(&self) -> Value {
26 json!({
27 "type": "object",
28 "properties": {
29 "command": {
30 "type": "string",
31 "description": "The PowerShell command to execute"
32 },
33 "timeout": {
34 "type": "integer",
35 "description": "Optional timeout in seconds"
36 }
37 },
38 "required": ["command"]
39 })
40 }
41
42 fn permission_level(&self) -> PermissionLevel {
43 PermissionLevel::Dangerous
44 }
45
46 fn interrupt_behavior(&self) -> InterruptBehavior {
47 InterruptBehavior::Cancel
48 }
49
50 async fn execute(&self, input: Value) -> AppResult<String> {
51 let command = input
52 .get("command")
53 .and_then(|v| v.as_str())
54 .ok_or_else(|| claude_rust_errors::AppError::Tool("missing 'command' field".into()))?;
55
56 let _timeout = input
57 .get("timeout")
58 .and_then(|v| v.as_u64());
59
60 tracing::info!(command, "executing PowerShell");
61
62 let program = if cfg!(target_os = "windows") {
64 "powershell.exe"
65 } else {
66 "pwsh"
67 };
68
69 let output = Command::new(program)
70 .arg("-Command")
71 .arg(command)
72 .output()
73 .await
74 .map_err(|e| claude_rust_errors::AppError::Tool(
75 format!("failed to spawn {program}: {e}")
76 ))?;
77
78 let stdout = String::from_utf8_lossy(&output.stdout);
79 let stderr = String::from_utf8_lossy(&output.stderr);
80
81 let mut result = String::new();
82 if !stdout.is_empty() {
83 result.push_str(&stdout);
84 }
85 if !stderr.is_empty() {
86 if !result.is_empty() {
87 result.push('\n');
88 }
89 result.push_str("STDERR:\n");
90 result.push_str(&stderr);
91 }
92 if result.is_empty() {
93 result.push_str("(no output)");
94 }
95
96 if result.len() > 100_000 {
97 result.truncate(100_000);
98 result.push_str("\n... (truncated)");
99 }
100
101 Ok(result)
102 }
103}