agent_engine/tools/
powershell.rs1use super::{bash::ShellSpec, Tool, ToolContext};
10use crate::{Result, RuntimeError};
11use serde_json::{json, Value};
12
13pub struct PowerShellTool;
14
15#[async_trait::async_trait]
16impl Tool for PowerShellTool {
17 fn origin(&self) -> crate::tools::ToolOrigin {
18 crate::tools::ToolOrigin::Builtin
19 }
20
21 fn effect(&self) -> crate::tools::catalog::ToolEffect {
23 crate::tools::catalog::ToolEffect::NonIdempotent
24 }
25
26 fn name(&self) -> &str {
27 "powershell"
28 }
29
30 fn description(&self) -> &str {
31 "Execute a PowerShell command on the Windows host. Use this for Windows-native tasks: \
32 file system operations on C:\\ paths, registry, services, processes, Windows APIs. \
33 PowerShell quoting rules apply — single quotes are literal, double quotes interpolate \
34 ($var, `n). Native cmdlets like Get-Process, Get-ChildItem, Test-Path are available. \
35 Commands time out after 30 seconds by default; pass a larger timeout when needed."
36 }
37
38 fn parameters(&self) -> Value {
39 json!({
40 "type": "object",
41 "properties": {
42 "command": {
43 "type": "string",
44 "description": "The PowerShell command to execute"
45 },
46 "timeout": {
47 "type": "integer",
48 "description": "Timeout in seconds (default: 30). Use a larger value for long-running commands."
49 }
50 },
51 "required": ["command"]
52 })
53 }
54
55 async fn execute(&self, params: Value, ctx: ToolContext) -> Result<String> {
56 let command = params["command"]
57 .as_str()
58 .ok_or_else(|| RuntimeError::Tool("Missing command parameter".to_string()))?;
59
60 super::bash::run_shell_command(
61 command,
62 ShellSpec::PowerShell,
63 params["timeout"].as_u64(),
64 ctx,
65 None,
66 )
67 .await
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn powershell_tool_schema() {
77 let tool = PowerShellTool;
78 assert_eq!(tool.name(), "powershell");
79 let params = tool.parameters();
80 assert!(params["properties"]["command"].is_object());
81 assert!(params["required"]
82 .as_array()
83 .unwrap()
84 .contains(&json!("command")));
85 }
86
87 #[cfg(windows)]
88 #[tokio::test]
89 async fn powershell_executes_simple_command() {
90 let tool = PowerShellTool;
91 let ctx = super::super::test_helpers::create_tool_context();
92 let result = tool
93 .execute(json!({ "command": "Write-Output hello-ps" }), ctx)
94 .await
95 .unwrap();
96 assert!(result.contains("hello-ps"), "got: {result}");
97 }
98}