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 )
66 .await
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn powershell_tool_schema() {
76 let tool = PowerShellTool;
77 assert_eq!(tool.name(), "powershell");
78 let params = tool.parameters();
79 assert!(params["properties"]["command"].is_object());
80 assert!(params["required"]
81 .as_array()
82 .unwrap()
83 .contains(&json!("command")));
84 }
85
86 #[cfg(windows)]
87 #[tokio::test]
88 async fn powershell_executes_simple_command() {
89 let tool = PowerShellTool;
90 let ctx = super::super::test_helpers::create_tool_context();
91 let result = tool
92 .execute(json!({ "command": "Write-Output hello-ps" }), ctx)
93 .await
94 .unwrap();
95 assert!(result.contains("hello-ps"), "got: {result}");
96 }
97}