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 Runs in PowerShell 7 (pwsh) when installed, else Windows PowerShell 5.1; don't start \
34 another powershell or pwsh inside it. Scripts may run: call them directly, e.g. \
35 .\\test.ps1. PowerShell quoting rules apply — single quotes are literal, double quotes \
36 interpolate ($var, `n). Native cmdlets like Get-Process, Get-ChildItem, Test-Path are \
37 available. Commands time out after 30 seconds by default; pass a larger timeout when \
38 needed."
39 }
40
41 fn parameters(&self) -> Value {
42 json!({
43 "type": "object",
44 "properties": {
45 "command": {
46 "type": "string",
47 "description": "The PowerShell command to execute"
48 },
49 "timeout": {
50 "type": "integer",
51 "description": "Timeout in seconds (default: 30). Use a larger value for long-running commands."
52 }
53 },
54 "required": ["command"]
55 })
56 }
57
58 async fn execute(&self, params: Value, ctx: ToolContext) -> Result<String> {
59 let command = params["command"]
60 .as_str()
61 .ok_or_else(|| RuntimeError::Tool("Missing command parameter".to_string()))?;
62
63 super::bash::run_shell_command(
64 command,
65 ShellSpec::PowerShell,
66 params["timeout"].as_u64(),
67 ctx,
68 None,
69 )
70 .await
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn powershell_tool_schema() {
80 let tool = PowerShellTool;
81 assert_eq!(tool.name(), "powershell");
82 let params = tool.parameters();
83 assert!(params["properties"]["command"].is_object());
84 assert!(params["required"]
85 .as_array()
86 .unwrap()
87 .contains(&json!("command")));
88 }
89
90 #[cfg(windows)]
91 #[tokio::test]
92 async fn powershell_executes_simple_command() {
93 let tool = PowerShellTool;
94 let ctx = super::super::test_helpers::create_tool_context();
95 let result = tool
96 .execute(json!({ "command": "Write-Output hello-ps" }), ctx)
97 .await
98 .unwrap();
99 assert!(result.contains("hello-ps"), "got: {result}");
100 }
101}