Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Shell Mode

Let zo inspect and execute commands inside your workspace with --shell.

Unlike STDIN pipelines, shell mode gives the model live command execution tools. Approved commands run with your normal user permissions, so treat --shell as a higher-trust feature.

Basic Usage

# Single-shot shell run
zo --shell 'Run the test suite in this repo and summarize failures'
 
# Shell plus file context
zo --files read --shell @Cargo.toml 'Inspect this crate and run the relevant tests'
 
# Interactive shell-enabled chat
zo --chat --shell 'Help me debug this project step by step'

--shell works in both normal single-shot mode and --chat. It can also be combined with file tools such as --files read or --files write.

How It Works

  • zo only exposes shell execution tools when you pass --shell.
  • The model prefers direct program execution for simple commands and only falls back to a full shell command when it needs shell syntax.
  • Commands start in the current workspace by default. The working directory can move into relative subdirectories, but not outside the workspace.
  • Output is bounded automatically. By default zo allows up to 30 seconds and about 24k characters of combined output, with a hard timeout cap of 120 seconds.

Approval Model

Shell mode is policy-driven.

  • The default shell action is ask, so commands prompt for approval unless a rule allows or denies them first.
  • --non-interactive keeps policy evaluation but turns anything that would have asked into a denial.
  • --hidden removes the extra approval gate for dotfiles and hidden directories, but normal policy checks still apply.
  • --verbose shows shell tool calls as the model makes them. --debug shows the same information before the request is sent.

Some command shapes always require approval even when a policy would otherwise allow them:

  • Shell variable expansion such as $HOME
  • Wildcard expansion such as *.rs
  • Command chaining with &&, ||, or ;
  • Redirection with <, >, or >>
  • Inline environment assignments such as FOO=bar cmd
  • Hidden paths without --hidden
  • Explicit paths outside the current workspace

Policies

Shell rules live in the [shell] section of ~/.config/zo/config.toml.

[shell]
default_action = "ask"
allowed_shells = ["/bin/sh", "/bin/bash", "/bin/zsh"]
 
[[shell.policy_sets]]
name = "git_safe"
 
[[shell.policy_sets.entries]]
action = "allow"
terminal = true
program = "git"
args_prefix = [{ exact = "status" }]

Activate named policy sets with --policies:

zo --shell --policies git_safe 'Check git status and explain what changed'

Policy evaluation order is:

  1. shell.always_on
  2. Named sets from --policies, in the order passed on the command line
  3. default_action if nothing matches

See Configuration for the full policy syntax.

Supported Shell Syntax

Shell mode supports ordinary single-line commands with quoting, pipes, chaining, and redirection.

These forms are rejected before execution:

  • Multiline shell input
  • Background execution with &
  • Command substitution with backticks or $()
  • Heredocs, here-strings, and process substitution
  • Unbalanced quotes

Common Examples

# Run tests and summarize the result
zo --shell 'Run cargo test and explain any failures'
 
# Combine code context with shell access
zo --files read --shell @src/main.rs 'Review the startup path, then run the relevant tests'
 
# Use shell mode in a conversation
zo --chat --shell /debugger 'Help me track down a flaky test'
> Run the failing test a few times
> Inspect the relevant source files
> exit
 
# Show tool calls while they happen
zo --shell --verbose 'Look for large files in this repo and summarize the result'

When to Use Shell Mode

Use --shell when the model needs fresh command output from the current workspace, such as:

  • Running tests and summarizing failures
  • Inspecting git state before a review
  • Searching the repo with find, rg, or build tools
  • Reproducing a local issue inside a chat session

If you already have the output you want to analyze, plain STDIN pipelines are simpler:

cargo test 2>&1 | zo 'Explain these failures'

Use STDIN Pipelines when command execution should stay fully in your control, and use Shell Integration for shell helper functions and workflow ideas.