shell(
command,
work_dir=None,
timeout=None,
ignore_errors=False,
)
Execute shell commands with proper STDIO capture.
USE THIS TOOL: - For running read-only shell commands (ls, git log, wc, etc.) - When you need command output for analysis
DO NOT USE: - For reading file contents (use read_file_bounded instead) - For searching code (use rg_search instead) - For modifying files, network access, or running arbitrary scripts
Security: Commands are validated against an allowlist of read-only programs. Shell operators (pipes, redirects, chaining) are blocked. Git commands are restricted to read-only subcommands.
Parameters:
| Name | Type | Description | Default |
command | str | list[str] | Shell command string or list of commands to execute sequentially. | required |
work_dir | str | None | Working directory for command execution (default: current dir). | None |
timeout | int | None | Timeout in seconds for command execution (default: 900). | None |
ignore_errors | bool | If True, continue on errors and return success (default: False). | False |
Returns:
| Type | Description |
dict[str, Any] | Dict with status and content blocks. |
Example
shell("ls -la") shell(["git status", "git diff"], work_dir="/repo")
Source code in src/code_context_agent/tools/shell_tool.py
| @tool
def shell(
command: str | list[str],
work_dir: str | None = None,
timeout: int | None = None,
ignore_errors: bool = False,
) -> dict[str, Any]:
"""Execute shell commands with proper STDIO capture.
USE THIS TOOL:
- For running read-only shell commands (ls, git log, wc, etc.)
- When you need command output for analysis
DO NOT USE:
- For reading file contents (use read_file_bounded instead)
- For searching code (use rg_search instead)
- For modifying files, network access, or running arbitrary scripts
Security: Commands are validated against an allowlist of read-only programs.
Shell operators (pipes, redirects, chaining) are blocked. Git commands are
restricted to read-only subcommands.
Args:
command: Shell command string or list of commands to execute sequentially.
work_dir: Working directory for command execution (default: current dir).
timeout: Timeout in seconds for command execution (default: 900).
ignore_errors: If True, continue on errors and return success (default: False).
Returns:
Dict with status and content blocks.
Example:
>>> shell("ls -la")
>>> shell(["git status", "git diff"], work_dir="/repo")
"""
timeout = timeout or DEFAULT_TIMEOUT
work_dir = work_dir or str(Path.cwd())
commands = [command] if isinstance(command, str) else command
results: list[CommandResult] = []
for cmd in commands:
violation = _validate_command(cmd)
if violation:
logger.warning(f"Shell command blocked: {violation}")
results.append(CommandResult(command=cmd, exit_code=-1, stdout="", stderr=violation))
if not ignore_errors:
break
continue
result = _execute(cmd, work_dir, timeout)
results.append(result)
if not result.success and not ignore_errors:
break
# Build response
success_count = sum(1 for r in results if r.success)
content = [
{
"text": f"Execution Summary:\nTotal commands: {len(results)}\n"
f"Successful: {success_count}\nFailed: {len(results) - success_count}",
},
]
for r in results:
parts = [f"Command: {r.command}", f"Status: {r.status}", f"Exit Code: {r.exit_code}"]
if r.stdout:
parts.append(f"Output:\n{r.stdout}")
if r.stderr:
parts.append(f"Error:\n{r.stderr}")
content.append({"text": "\n".join(parts)})
has_errors = any(not r.success for r in results)
return {"status": "error" if has_errors and not ignore_errors else "success", "content": content}
|