Shell Tool
Execute arbitrary shell commands in the user's environment.
Overview
The shell tool allows agents to execute arbitrary shell commands synchronously. This is one of the most powerful tools — it lets agents run builds, install dependencies, query APIs, and interact with the system. Each call runs in a fresh, isolated shell session — no state persists between calls.
Commands have a default 30-second timeout and require user confirmation unless --yolo is used. For servers, watchers, and other long-running commands, add the background_jobs toolset alongside shell.
Shell interpreter detection
The shell tool automatically detects and names the resolved shell interpreter (e.g., bash, zsh, powershell, pwsh, cmd) in its description to the model, along with the operating system (Linux, macOS, Windows). This helps models use the correct shell syntax for the host environment.
For example:
- On Linux with bash: "Executes the given shell command with bash on Linux."
- On Windows with PowerShell: "Executes the given shell command with powershell on Windows. Use Windows PowerShell 5.1 syntax: chain commands with ";" (not "&&"), and avoid POSIX commands/flags like "ls -la"."
This reduces wasted turns where models assume POSIX syntax on Windows or vice versa.
Configuration
toolsets:
- type: shellOptions
| Property | Type | Description |
|---|---|---|
env | object | Environment variables to set for all shell commands |
safer | boolean | Deprecated and ignored — shell commands are always classified now (see Command classification). Kept so existing YAMLs still parse. |
sudo_askpass | boolean | Opt in to prompting for a sudo password (see Sudo support). Default false. |
Custom Environment Variables
toolsets:
- type: shell
env:
MY_VAR: "value"
PATH: "${env.PATH}:/custom/bin"Command classification
Every shell command is classified against an embedded taxonomy before the approval decision — no opt-in required:
- Destructive matches (
rm -rf <path>,docker volume rm,mkfs,dd if=… of=/dev/<disk>, …) are labelleddestructivewith ablast_radius(low/medium/high) and acategorytag. The TUI confirmation dialog renders the blast radius with a color badge. - Known-safe reads (
ls,cat,git status,git diff,docker ps,docker logs,kubectl get, …) are labelledsafe. - Everything else is labelled
unknown.
The session's safety mode decides what each label means: strict asks about everything, balanced auto-runs safe commands and asks about destructive/unknown ones, autonomous runs everything. Custom permission rules always win over the mode.
Compound shell (a && b, a; b, a | b) is never matched against the safe allowlist; any destructive segment falls through to ask. The full taxonomy lives in pkg/safety/safety_patterns.json.
See examples/safety_modes.yaml for a full example. The legacy safer: true toolset flag is deprecated and ignored.
Sudo support
By default a shell command has no controlling terminal, so a sudo command that needs a password hangs until it times out (the agent usually gives up and falls back to printing manual instructions).
Set sudo_askpass: true to enable a sudo privilege escalation flow:
toolsets:
- type: shell
sudo_askpass: trueWhen enabled, sudo commands prompt you for your password through the host UI (the input is masked). The password is handed to sudo over a private, per-session socket via the standard SUDO_ASKPASS mechanism — it is never written to the command line, the logs, or stored by the agent.
The bridge environment variables (SUDO_ASKPASS, CAGENT_ASKPASS_SOCKET, CAGENT_ASKPASS_TOKEN) are added only to commands that invoke sudo, but within such a command they are visible to every child process, not just sudo. They carry a socket path and a session token, not the password; the socket lives in a 0700 directory, so only your own user can reach it.
Notes and limitations:
- Unix only. The flag has no effect on Windows.
- Interactive UI only. In headless / non-interactive runs the prompt is declined automatically and
sudofails as before. - Only a bare
sudo ...invocation in a POSIX shell (sh,bash,zsh, ...) is handled.sudocalled by absolute path (/usr/bin/sudo), viaenv sudo, from inside a nested script, or under a non-POSIX shell (e.g.fish) is not intercepted and behaves as before. - Caching is
sudo's own. Because each shell tool call runs in a fresh shell with no controlling terminal,sudo's credential cache does not persist across separate tool calls: you are prompted once per shell command that usessudo. Within a single command, multiplesudocalls (e.g.sudo a && sudo b) usually share one prompt, subject tosudo's own timestamp configuration. - The prompt must be answered within the command's timeout; raise the
timeoutparameter forsudocommands that may wait on input. - Prompts are serialized: if a single command runs two
sudocalls in parallel (e.g.sudo a & sudo b), the second waits for the first prompt to be answered rather than opening two dialogs at once.
Available Tools
The shell toolset exposes one tool:
| Tool Name | Description |
|---|---|
shell | Run a command synchronously and return its combined output when it finishes. |
shell parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
cmd | string | ✓ | The shell command to execute. |
cwd | string | ✗ | Working directory to run the command in (default: .). |
timeout | integer | ✗ | Per-call execution timeout in seconds (default: 30). |
WarningSafety
The shell tool gives agents full access to the system shell. Always set
max_iterationson agents that use the shell tool to prevent infinite loops. A value of 20–50 is typical for development agents. Use Sandbox Mode for additional isolation.
NoteTool Confirmation
By default, Docker Agent asks for user confirmation before executing shell commands. Use
--yoloto auto-approve all tool calls.