Shell functions provide a convenient way to set machine-specific defaults and create shorthand commands for running agents with Safehouse.
Why Use Shell Wrappers?
If you work across multiple projects with shared dependencies, caches, or team folders in machine-specific locations, you’ll want to keep those settings out of project config files.
Shell wrappers let you:
Define machine-local defaults once
Create convenient shortcuts for common workflows
Keep machine-specific paths out of shared repositories
Reuse configurations across multiple agents and tools
Basic Pattern
Add this to your shell configuration file (~/.zshrc, ~/.bashrc, or equivalent):
# Generic safe wrapper with your machine defaults
safe () {
safehouse \
--add-dirs-ro= " $HOME /server" \
--add-dirs-ro= " $HOME /shared-libs" \
" $@ "
}
# Agent-specific shortcuts
safe-claude () { safe claude --dangerously-skip-permissions " $@ " }
safe-cursor () { safe --enable=clipboard,macos-gui cursor " $@ " }
safe-aider () { safe aider " $@ " }
Now you can run:
safe-claude # Claude with your machine defaults
safe-cursor # Cursor with clipboard and GUI
safe-aider # Aider with your defaults
Advanced Pattern with Append Profile
For machine-local policy exceptions that can’t be expressed with --add-dirs alone, use --append-profile:
Create a local overrides profile
Create a Sandbox Profile file for machine-specific rules: ~/.config/agent-safehouse/local-overrides.sb
;; Machine-local policy overrides
;; These rules are appended last and override everything else
(allow file-read*
(home-literal "/.gitignore_global" )
(home-subpath "/Library/Application Support/CleanShot/media" )
(subpath "/Volumes/Shared/Engineering" )
)
(allow file-write*
(home-subpath "/Documents/agent-logs" )
)
Set up shell function
Add this to your ~/.zshrc: export SAFEHOUSE_APPEND_PROFILE = " $HOME /.config/agent-safehouse/local-overrides.sb"
safe () {
safehouse \
--add-dirs-ro= " $HOME /server" \
--append-profile= " $SAFEHOUSE_APPEND_PROFILE " \
" $@ "
}
safe-claude () { safe claude --dangerously-skip-permissions " $@ " }
Use your wrapper
All invocations now include your machine-local overrides: safe-claude # Includes local-overrides.sb
safe -- aider # Also includes local-overrides.sb
Use --add-dirs-ro/--add-dirs for normal folder access. Reserve --append-profile for machine-local policy exceptions or final deny/allow overrides.
Complete Example
Here’s a full-featured setup with multiple shortcuts:
# Machine-specific configuration
export SAFEHOUSE_APPEND_PROFILE = " $HOME /.config/agent-safehouse/local-overrides.sb"
export SHARED_LIBS = " $HOME /shared-libs"
export TEAM_RESOURCES = "/Volumes/Shared/Engineering"
# Base safe wrapper with machine defaults
safe () {
safehouse \
--add-dirs-ro= " $SHARED_LIBS " \
--append-profile= " $SAFEHOUSE_APPEND_PROFILE " \
" $@ "
}
# Agent shortcuts
safe-claude () {
safe claude --dangerously-skip-permissions " $@ "
}
safe-cursor () {
safe --enable=clipboard,macos-gui cursor " $@ "
}
safe-aider () {
safe --enable=clipboard aider " $@ "
}
safe-codex () {
safe codex " $@ "
}
# Workflow-specific wrappers
safe-docker () {
safe --enable=docker " $@ "
}
safe-k8s () {
safe --enable=kubectl " $@ "
}
# Review mode (read-only, no workdir write access)
safe-review () {
local project_path = " ${1 :- . } "
shift || true
safe --workdir= "" --add-dirs-ro= " $project_path " -- " $@ "
}
Usage examples:
safe-claude # Claude with machine defaults
safe-cursor # Cursor with GUI + clipboard
safe-aider --model gpt-4 # Aider with defaults, custom args
safe-docker -- docker compose up
safe-k8s -- kubectl get pods
safe-review ~/other-project claude # Read-only access
Per-Project Wrappers
For projects with unique requirements, create project-specific scripts:
~/my-project/scripts/safe-test.sh
#!/bin/bash
# Project-specific test runner with Safehouse
set -euo pipefail
cd "$( dirname " $0 ")/.."
safehouse \
--add-dirs-ro= " $HOME /test-fixtures" \
--add-dirs= "/tmp/test-output" \
--enable=docker \
--env=.env.test \
-- npm test " $@ "
Make it executable:
chmod +x ~/my-project/scripts/safe-test.sh
Run it:
Environment Variable Patterns
Use environment variables for settings that apply to all Safehouse invocations:
# Always trust workdir config in your workspace directories
export SAFEHOUSE_TRUST_WORKDIR_CONFIG = 1
# Always grant read access to shared resources
export SAFEHOUSE_ADD_DIRS_RO = " $HOME /shared: $HOME /references"
# Always pass through these environment variables
export SAFEHOUSE_ENV_PASS = "AWS_PROFILE,GCP_PROJECT"
These apply even when you invoke safehouse directly without wrapper functions.
Be careful with SAFEHOUSE_TRUST_WORKDIR_CONFIG=1. This trusts .safehouse config files in all directories you run Safehouse from, including untrusted repositories.
App Launchers
You can use shell functions to launch GUI apps through Safehouse:
safe-claude-app () {
safehouse --enable=macos-gui -- open -a "Claude"
}
safe-cursor-app () {
safehouse --enable=clipboard,macos-gui -- open -a "Cursor"
}
Usage:
safe-claude-app # Launches Claude.app in sandbox
safe-cursor-app # Launches Cursor.app in sandbox
For a better app launch experience, consider using the prebuilt launcher scripts in dist/ or creating .command files that macOS can execute directly.
Debugging Your Setup
Use --explain to verify your wrapper configuration:
safe-claude --explain --stdout | head -20
This shows:
All path grants (from shell function and environment)
Loaded agent profiles
Optional integrations
Appended profile files
Tips and Best Practices
Keep It DRY Define common options once in a base safe() function. Derive agent-specific functions from it.
Machine-Local Only Put shell functions in ~/.zshrc or ~/.bashrc, not in project repositories. These are machine-specific.
Use Append Profiles For complex machine-local rules, use --append-profile with a .sb file instead of long CLI arguments.
Document Assumptions Add comments in your shell config explaining what paths like $SHARED_LIBS or $TEAM_RESOURCES are for.