1- """Bash command execution tool with background process management."""
1+ """Shell command execution tool with background process management.
2+
3+ Supports both bash (Unix/Linux/macOS) and PowerShell (Windows).
4+ """
25
36import asyncio
7+ import platform
48import re
59import time
610import uuid
@@ -211,15 +215,45 @@ async def terminate(cls, bash_id: str) -> BackgroundShell:
211215
212216
213217class BashTool (Tool ):
214- """Execute bash commands in foreground or background."""
218+ """Execute shell commands in foreground or background.
219+
220+ Automatically detects OS and uses appropriate shell:
221+ - Windows: PowerShell
222+ - Unix/Linux/macOS: bash
223+ """
224+
225+ def __init__ (self ):
226+ """Initialize BashTool with OS-specific shell detection."""
227+ self .is_windows = platform .system () == "Windows"
228+ self .shell_name = "PowerShell" if self .is_windows else "bash"
215229
216230 @property
217231 def name (self ) -> str :
218232 return "bash"
219233
220234 @property
221235 def description (self ) -> str :
222- return """Execute bash commands in foreground or background.
236+ shell_examples = {
237+ "Windows" : """Execute PowerShell commands in foreground or background.
238+
239+ For terminal operations like git, npm, docker, etc. DO NOT use for file operations - use specialized tools.
240+
241+ Parameters:
242+ - command (required): PowerShell command to execute
243+ - timeout (optional): Timeout in seconds (default: 120, max: 600) for foreground commands
244+ - run_in_background (optional): Set true for long-running commands (servers, etc.)
245+
246+ Tips:
247+ - Quote file paths with spaces: cd "My Documents"
248+ - Chain dependent commands with semicolon: git add . ; git commit -m "msg"
249+ - Use absolute paths instead of cd when possible
250+ - For background commands, monitor with bash_output and terminate with bash_kill
251+
252+ Examples:
253+ - git status
254+ - npm test
255+ - python -m http.server 8080 (with run_in_background=true)""" ,
256+ "Unix" : """Execute bash commands in foreground or background.
223257
224258For terminal operations like git, npm, docker, etc. DO NOT use for file operations - use specialized tools.
225259
@@ -238,15 +272,18 @@ def description(self) -> str:
238272 - git status
239273 - npm test
240274 - python3 -m http.server 8080 (with run_in_background=true)"""
275+ }
276+ return shell_examples ["Windows" ] if self .is_windows else shell_examples ["Unix" ]
241277
242278 @property
243279 def parameters (self ) -> dict [str , Any ]:
280+ cmd_desc = f"The { self .shell_name } command to execute. Quote file paths with spaces using double quotes."
244281 return {
245282 "type" : "object" ,
246283 "properties" : {
247284 "command" : {
248285 "type" : "string" ,
249- "description" : "The bash command to execute. Quote file paths with spaces using double quotes." ,
286+ "description" : cmd_desc ,
250287 },
251288 "timeout" : {
252289 "type" : "integer" ,
@@ -268,10 +305,10 @@ async def execute(
268305 timeout : int = 120 ,
269306 run_in_background : bool = False ,
270307 ) -> ToolResult :
271- """Execute bash command with optional background execution.
308+ """Execute shell command with optional background execution.
272309
273310 Args:
274- command: The bash command to execute
311+ command: The shell command to execute
275312 timeout: Timeout in seconds (default: 120, max: 600)
276313 run_in_background: Set true to run command in background
277314
@@ -286,16 +323,31 @@ async def execute(
286323 elif timeout < 1 :
287324 timeout = 120
288325
326+ # Prepare shell-specific command execution
327+ if self .is_windows :
328+ # Windows: Use PowerShell with appropriate encoding
329+ shell_cmd = ["powershell.exe" , "-NoProfile" , "-Command" , command ]
330+ else :
331+ # Unix/Linux/macOS: Use bash
332+ shell_cmd = command
333+
289334 if run_in_background :
290335 # Background execution: Create isolated process
291336 bash_id = str (uuid .uuid4 ())[:8 ]
292337
293338 # Start background process with combined stdout/stderr
294- process = await asyncio .create_subprocess_shell (
295- command ,
296- stdout = asyncio .subprocess .PIPE ,
297- stderr = asyncio .subprocess .STDOUT , # Redirect stderr to stdout
298- )
339+ if self .is_windows :
340+ process = await asyncio .create_subprocess_exec (
341+ * shell_cmd ,
342+ stdout = asyncio .subprocess .PIPE ,
343+ stderr = asyncio .subprocess .STDOUT ,
344+ )
345+ else :
346+ process = await asyncio .create_subprocess_shell (
347+ shell_cmd ,
348+ stdout = asyncio .subprocess .PIPE ,
349+ stderr = asyncio .subprocess .STDOUT ,
350+ )
299351
300352 # Create background shell and add to manager
301353 bg_shell = BackgroundShell (bash_id = bash_id , command = command , process = process , start_time = time .time ())
@@ -319,11 +371,18 @@ async def execute(
319371
320372 else :
321373 # Foreground execution: Create isolated process
322- process = await asyncio .create_subprocess_shell (
323- command ,
324- stdout = asyncio .subprocess .PIPE ,
325- stderr = asyncio .subprocess .PIPE ,
326- )
374+ if self .is_windows :
375+ process = await asyncio .create_subprocess_exec (
376+ * shell_cmd ,
377+ stdout = asyncio .subprocess .PIPE ,
378+ stderr = asyncio .subprocess .PIPE ,
379+ )
380+ else :
381+ process = await asyncio .create_subprocess_shell (
382+ shell_cmd ,
383+ stdout = asyncio .subprocess .PIPE ,
384+ stderr = asyncio .subprocess .PIPE ,
385+ )
327386
328387 try :
329388 stdout , stderr = await asyncio .wait_for (process .communicate (), timeout = timeout )
0 commit comments