The cuti dev container provides a fully configured development environment with cuti, Claude Code mode as the default, and an explicit OpenClaw mode for the OpenClaw runtime plus configured add-ons.
# Start an interactive container session (works from any directory)
cuti container
# Run a specific command in the container
cuti container "cuti web"
cuti container "claude 'Explain this project'"- Separate Linux credentials in
~/.cuti/claude-linux/ - No conflicts with macOS Keychain
- One-time authentication for all containers
- 📚 Complete Authentication Guide
- Claude Code mode is enabled by default for
cuti container - OpenClaw mode is opt-in with
cuti container --openclaworcuti container --claw - OpenClaw mode starts with OpenClaw only; add-ons are read from
cuti providers enable ... - Provider-specific auth, config, and skills directories are mounted automatically
- Provider CLIs are installed through their current standalone/native install paths at container startup
- Claude Code mode refreshes Claude Code into the persistent provider runtime on each
cuti containerstartup - Hermes is available as an experimental provider track in cuti
-
Universal Container (
cuti-dev-universal): Used when running from any project directory- Installs cuti from PyPI via
uv tool install - Perfect for using cuti with any project
- Pre-configured with all tools
- Installs cuti from PyPI via
-
Development Container (
cuti-dev-cuti): Used when running from cuti source directory- Installs cuti from local source code
- Ideal for cuti development and testing
- Includes development dependencies
- Custom prompt:
cuti:~/path $for clear container identification - Pre-configured with oh-my-zsh for better terminal experience
- All cuti commands available immediately
- Python 3.11, Node.js 20, and common development tools pre-installed
- Docker CLI installed in container (not the daemon)
- Host Docker socket mounted at
/var/run/docker.sock - Automatic sudo wrapper if needed for permissions
- Run Docker commands that execute on host's Docker daemon
- Build images, run containers, use docker-compose - all from within the container
- Perfect for projects that need containerization during development
# Install Colima (recommended Docker alternative for Mac)
brew install colima
# Start Colima
colima start
# Or use Docker Desktop
# Download from https://www.docker.com/products/docker-desktop# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker- Install Docker Desktop from https://www.docker.com/products/docker-desktop
- Or use WSL2 with Docker
# Start container from any project
cd ~/my-project
cuti container
# You'll see the custom prompt:
# cuti:/workspace $
# Inside container, all commands work:
cuti web # Start read-only ops console (accessible at http://localhost:8000)
cuti --help # Inspect the cuti CLI
cuti providers list # Inspect provider selection
claude --help # Use Claude CLI (already authenticated)# Run provider-native commands
cuti container "claude -p 'Review this code and suggest improvements'"
cuti container "codex exec 'summarize this repository'"
cuti container "cuti providers doctor"
# Run Claude directly
cuti container "claude 'What does this project do?'"
# Add providers to the default Claude Code container mode
cuti providers enable codex
cuti providers enable opencode
cuti providers enable hermes
cuti container --rebuild
cuti container "codex --version && opencode --version && hermes version"
# Start OpenClaw mode explicitly
cuti container --openclaw
# OpenClaw add-ons come from explicit provider config
cuti providers enable claude
cuti providers enable codex
cuti container --openclaw "openclaw --version && claude --version && codex --version"
# Run Python scripts
cuti container "python script.py"# Start the read-only ops console in container (accessible from host)
cuti container "cuti web"
# Then open http://localhost:8000 in your browser
# Run development servers
cuti container "npm run dev"
cuti container "python manage.py runserver"The container comes with Claude CLI pre-configured for native Claude permission modes:
- Default mode:
CUTI_CLAUDE_PERMISSION_MODE=auto - Supported modes:
default,acceptEdits,plan,auto,dontAsk,bypassPermissions - Explicit override: set
CUTI_CLAUDE_PERMISSION_MODEbefore starting the container
# Default: asks Claude Code to use auto mode when available
cuti container "claude 'Explain this code'"
# Stricter file-editing behavior
CUTI_CLAUDE_PERMISSION_MODE=acceptEdits cuti container "claude 'Review this diff'"
# Legacy isolated-container behavior, only when explicitly requested
CUTI_CLAUDE_PERMISSION_MODE=bypassPermissions cuti container "claude 'Run the local test suite'"- Claude Code now owns the permission/approval surface through native modes.
autoreduces routine prompts while keeping Claude's background safety checks.bypassPermissionsremains available for isolated containers, but it is not the default CUTI premise.- CUTI's differentiated role is provider runtime setup, state persistence, and observability.
If you're using an older container image, rebuild to get the permission-mode wrapper:
cuti container --rebuildClaude Code mode is the default for cuti container. OpenClaw mode is explicit and does not join the default container just because OpenClaw has been enabled in provider config:
cuti providers list
cuti providers doctor
# Default Claude Code mode. Claude Code is refreshed for subsequent containers.
cuti container
# Explicit OpenClaw mode.
cuti container --openclaw
# OpenClaw mode add-ons.
cuti providers enable claude
cuti providers enable codex
cuti providers enable opencode
cuti providers enable hermes
cuti container --openclaw
cuti providers auth claude --login
cuti openclaw onboard
cuti openclaw up
cuti providers update codex
cuti providers update openclaw
cuti providers update hermesThe standard cloud profile exports provider metadata into the container:
CUTI_CONTAINER_MODEisclaude-codeoropenclawCUTI_AGENT_PROVIDERScontains the enabled provider IDsCUTI_PRIMARY_AGENT_PROVIDERisclaudein Claude Code mode andopenclawin OpenClaw modeCODEX_HOME,OPENCLAW_STATE_DIR,OPENCLAW_CONFIG_PATH,OPENCLAW_PREFIX,HERMES_HOME,XDG_CONFIG_HOME, andXDG_DATA_HOMEare set for provider state
Provider-specific mounts are added automatically when the provider is enabled:
| Provider | Host Path | Container Path | Purpose |
|---|---|---|---|
| Claude | ~/.cuti/claude-linux/ |
/home/cuti/.claude-linux |
Linux Claude credentials |
| Provider runtimes | ~/.cuti/provider-runtimes/ |
/home/cuti/.cuti-providers |
Persistent provider CLI installs |
| Claude | ~/.claude/ |
/home/cuti/.claude-macos |
macOS config reference (read-only) |
| Codex | ~/.codex/ |
/home/cuti/.codex |
Auth, config, skills |
| OpenCode | ~/.opencode/ |
/home/cuti/.opencode |
CLI install and provider state |
| OpenCode | ~/.config/opencode/ |
/home/cuti/.config/opencode |
Config |
| OpenCode | ~/.local/share/opencode/ |
/home/cuti/.local/share/opencode |
Data |
| OpenClaw | ~/.openclaw/ |
/home/cuti/.openclaw |
Gateway config, credentials, agents, plugins, browser, voice-call, logs, media |
| Hermes Agent | ~/.hermes/ |
/home/cuti/.hermes |
Config, .env, memory, skills, sessions, gateway state |
| Hermes/OpenClaw migration | ~/.openclaw/ |
/home/cuti/.openclaw |
Read-only migration source when Hermes is enabled without OpenClaw |
| Hermes Claude auth reuse | ~/.claude/ |
/home/cuti/.claude |
Read-only Claude Code credentials for Hermes native Anthropic auth |
| Shared agent files | ~/.agents/ |
/home/cuti/.agents |
Shared provider skills/prompts |
Install behavior is provider-specific:
- Claude uses the official native installer
- Codex uses the official standalone installer
- OpenCode uses the official install script
- OpenClaw uses the official local-prefix installer when available, falls back to npm prefix install, stores the CLI under
~/.cuti/provider-runtimes/openclaw/, and runsopenclaw doctor --non-interactiveafter install/update/bootstrap - Hermes Agent uses the official NousResearch installer with
HERMES_HOME=/home/cuti/.hermes
OpenClaw-specific notes:
cuti openclaw ...runs the dedicated OpenClaw command group from the host.cuti openclaw onboardrunsopenclaw onboard --install-daemonin the container, then runsopenclaw doctor --non-interactive.cuti openclaw upruns onboarding if no OpenClaw state is detected, runs doctor, then starts the gateway in the foreground.- Channel, browser, plugin, voice-call, and dashboard surfaces are exposed as direct wrappers:
cuti openclaw channels-login,cuti openclaw channels ...,cuti openclaw browser ...,cuti openclaw plugins ...,cuti openclaw voice-setup,cuti openclaw voicecall ..., andcuti openclaw dashboard .... - Source-backed OpenClaw command families are also exposed directly: setup/config/configure, backup/reset/uninstall, message/agent/agents, status/health/sessions, tasks/flows/cron, models/infer/capability, ACP/MCP, approvals/exec-policy, nodes/devices/node, sandbox, TUI/chat/terminal, DNS/docs/proxy, hooks/webhooks, QR/pairing/directory, security/secrets/skills, update/completion, memory, and wiki.
- Future plugin command roots remain supported through
cuti openclaw run <command> ..., which forwards raw arguments to the installed OpenClaw CLI after cuti has enabled the provider and mounted state. - Managed OpenClaw state persists under
~/.openclaw; the CLI/runtime install persists separately under~/.cuti/provider-runtimes/openclaw. - cuti sets OpenClaw's container environment to the mounted state tree:
OPENCLAW_HOME=/home/cuti,OPENCLAW_STATE_DIR=/home/cuti/.openclaw,OPENCLAW_CONFIG_PATH=/home/cuti/.openclaw/openclaw.json,OPENCLAW_OAUTH_DIR=/home/cuti/.openclaw/credentials,OPENCLAW_PREFIX=/home/cuti/.cuti-providers/openclaw,NPM_CONFIG_PREFIX=/home/cuti/.cuti-providers/openclaw,npm_config_prefix=/home/cuti/.cuti-providers/openclaw, andPLAYWRIGHT_BROWSERS_PATH=/home/cuti/.openclaw/browser/browsers. - Containerized browser automation works best with OpenClaw-managed or remote-CDP browser profiles. Attaching to an already signed-in host browser still depends on host-local browser access.
- Voice-call setup installs/enables the official
@openclaw/voice-callplugin, but telephony providers still require their own credentials and reachable webhook/tunnel setup.
Hermes-specific notes:
- Hermes is marked experimental in
cuti providers list/status cuti providers auth hermes --loginrunshermes setup, which follows the upstream first-run wizard and can detect OpenClaw state for migrationcuti providers update hermesfollows Hermes' nativehermes updateflow inside the container, so upstream dependency refresh, config checks, and bundled skill sync happen the same way Hermes documents them- If
~/.openclawexists, runhermes claw migrate --dry-runinside the container to preview migration of OpenClaw persona, memory, skills, messaging settings, API keys, and workspace instructions before applying it - Hermes project context uses
.hermes.md,HERMES.md,AGENTS.md, andCLAUDE.md; durable personality belongs in~/.hermes/SOUL.md - Hermes profiles persist under
~/.hermes/profiles, and cuti recreates the lightweight~/.local/bin/<profile>wrappers on container startup so profile aliases continue to work after container restarts
Host-side provider commands:
cuti providers listshows selection plus setup summarycuti providers status <provider>shows detailed host/container statecuti providers doctorchecks readiness across providerscuti providers auth <provider> --loginlaunches the provider's interactive setup flow inside the containercuti providers update <provider>refreshes that provider in persistent provider runtime storage and updates active cuti cloud containers in place
# Build Docker images inside the container
cuti container "docker build -t myapp ."
# Run containers from within the container
cuti container "docker run -d redis:latest"
cuti container "docker ps"
# Use docker-compose
cuti container "docker-compose up -d"
# Interactive container with Docker support
cuti container
# Inside container:
docker images
docker run hello-world
docker-compose --version- Base:
python:3.11-bullseye - Cuti: Installed from PyPI via
uv tool install cuti - Agent providers: Claude preinstalled, other enabled providers installed at runtime
- Docker CLI: Installed for Docker-in-Docker support
- Tools: git, zsh, ripgrep, fd-find, bat, jq, curl, wget
- Python: uv package manager, pytest, httpx, fastapi, uvicorn
- Node.js: v20 with latest npm
- Shell: Zsh with oh-my-zsh and custom cuti prompt
- Same base as universal
- Cuti: Installed from local source code
- Includes all cuti development dependencies
- Used automatically when running from cuti source directory
The container automatically mounts:
| Host Path | Container Path | Purpose |
|---|---|---|
| Current directory | /workspace |
Your project files |
~/.cuti/ |
/home/cuti/.cuti-shared |
Shared cuti config and account state |
/var/run/docker.sock |
/var/run/docker.sock |
Docker socket for Docker-in-Docker |
Additional provider mounts are added only for enabled providers.
Automatically set in container:
CUTI_IN_CONTAINER=true- Indicates running in containerCUTI_AGENT_PROVIDERS- Comma-separated enabled providersCUTI_PRIMARY_AGENT_PROVIDER- Primary provider for prompts/help textCUTI_CLAUDE_PERMISSION_MODE=auto- Claude Code permission mode requested by the wrapperCLAUDE_CODE_ENABLE_AUTO_MODE=1- Enables Claude auto mode where the provider requires the flagCLAUDE_CONFIG_DIR=/home/cuti/.claude-linux- Linux Claude config locationCODEX_HOME=/home/cuti/.codex- Codex state rootHERMES_HOME=/home/cuti/.hermes- Hermes Agent config/state rootHERMES_INSTALL_DIR=/home/cuti/.hermes/hermes-agent- persisted Hermes Agent installationXDG_CONFIG_HOME=/home/cuti/.config- Shared XDG config rootXDG_DATA_HOME=/home/cuti/.local/share- Shared XDG data rootPYTHONUNBUFFERED=1- Python unbuffered outputTERM=xterm-256color- Color terminal supportANTHROPIC_API_KEY- Loaded from environment or file if available
Container runs with --network host for easy service access:
- Port 8000: cuti ops console
- Port 8080: Alternative web services
- Port 3000: Frontend dev servers
- Port 5000: Flask/FastAPI apps
- Port 5173: Vite dev server
When generating dev containers, cuti automatically detects:
| Project Type | Detection Files | Additional Tools |
|---|---|---|
| Python | pyproject.toml, requirements.txt |
pytest, black, ruff, mypy |
| JavaScript | package.json |
yarn, pnpm, typescript, nodemon |
| Full-stack | Both Python + JS files | All Python + JS tools |
| Ruby | Gemfile |
Ruby, Bundler |
| Go | go.mod |
Go 1.21+ |
| Rust | Cargo.toml |
Rust, Cargo |
| General | None of above | Basic dev tools |
Previous versions had an issue where exiting the container would break the Docker socket connection on macOS with Colima. This has been fixed by:
- Using
--initflag for proper signal handling - Installing only Docker CLI (not the full Docker engine)
- Adding proper signal traps for clean exit
If you experience this issue with an older version, restart Colima:
colima restart- Check Docker/Colima is running:
# For Colima
colima status
# For Docker
docker version- Start if needed:
# Colima (macOS)
colima start
# Docker Desktop - start from application- Verify image exists:
docker images | grep cuti-dev✅ Solved: Claude authentication persists across all containers using separate Linux credentials.
Quick Setup:
- Run
cuti container - Inside container, run
claude login(first time only) - Authentication persists for all future containers
How it works:
- Linux credentials stored in
~/.cuti/claude-linux/ - macOS credentials remain in Keychain (no conflicts)
- Settings copied from macOS automatically
📚 For detailed setup and troubleshooting, see Claude Container Authentication Guide
- Check which container is being used:
docker images | grep cuti-dev- Force rebuild:
# Remove old images
docker rmi cuti-dev-universal cuti-dev-cuti
# Rebuild
cuti container- Verify installation:
docker run --rm cuti-dev-universal which cuti
docker run --rm cuti-dev-universal cuti --helpThis occurs in non-interactive environments (like Claude Code). The container automatically detects and handles this - no action needed.
The container runs as root for simplicity. To fix file ownership after container use:
# On host after exiting container
sudo chown -R $(whoami) .If port 8000 is already in use:
# Find what's using the port
lsof -i :8000
# Kill the process or use different port
cuti container "cuti web --port 8001"How Docker access works in the container:
- The container automatically detects if Docker socket requires sudo
- If needed, creates a wrapper script that adds sudo automatically
- You can use
dockercommands normally - sudo is handled transparently
Docker commands work normally:
# Inside container - these all work
docker ps
docker build -t myapp .
docker-compose up -dIf Docker still shows "permission denied":
- The container should auto-configure this, but if not:
sudo docker ps # Test with sudo - Exit and restart the container - the wrapper will be created
Docker daemon not accessible:
- Ensure Docker/Colima is running on your host machine
- On macOS with Colima:
colima statusandcolima startif needed - On Linux:
sudo systemctl status docker
Colima won't start:
# Stop and clean
colima stop -f
colima delete
# Start with specific settings
colima start --arch aarch64 --vm-type vz --cpu 2 --memory 4Docker commands fail after Colima start:
# Check Docker context
docker context ls
# Set to use Colima
docker context use colimaAfter generating dev container files:
# Generate container configuration
cuti devcontainer generate
# Edit .devcontainer/Dockerfile to add custom tools
# Then rebuild
docker build -t my-custom-cuti -f .devcontainer/Dockerfile .- Install "Dev Containers" extension
- Run
cuti devcontainer generatein your project - Command Palette: "Dev Containers: Reopen in Container"
- VS Code uses the generated configuration
# Database in background
cuti container "docker run -d postgres:15"
# Redis
cuti container "docker run -d redis:7"
# Your app
cuti container "cuti web"Create named volumes for persistence:
# Create volume
docker volume create myproject-data
# Use in container
docker run -v myproject-data:/data ...# Interactive container
cuti container
# Run specific command
cuti container "COMMAND"
# Generate dev container files
cuti devcontainer generate [--type TYPE]
# Clean up
cuti devcontainer clean# List cuti images
docker images | grep cuti
# Remove cuti images
docker rmi cuti-dev-universal cuti-dev-cuti
# Clean all Docker resources
docker system prune -a
# Check container logs
docker logs [container-name]
# Execute command in running container
docker exec -it [container-name] bash- Detection: Determines if running from cuti source or other project
- Image Selection: Chooses
cuti-dev-universalorcuti-dev-cuti - Build Check: Checks if image exists, builds if missing
- Mount Setup: Configures volume mounts for project and configs
- Environment: Sets environment variables
- Execution: Runs container with appropriate flags
- Dockerfile templates:
src/cuti/services/devcontainer.py - Universal Dockerfile:
.devcontainer/Dockerfile.universal - Development Dockerfile:
.devcontainer/Dockerfile - Generated files:
.devcontainer/in your project
- Containers do not run with
--privileged - Claude uses native permission modes via
CUTI_CLAUDE_PERMISSION_MODE;bypassPermissionsis opt-in only - Config directories mounted with appropriate permissions
- No telemetry or external connections
- Local-first approach
To improve dev container support:
- Edit
src/cuti/services/devcontainer.py - Update Dockerfile templates
- Test with both container types:
# Test universal container cd ~/some-project cuti container # Test development container cd ~/cuti-source cuti container
- Submit PR with test results
- Legacy Todo Helpers - Local todo helpers and legacy queue conversion
- Legacy Queue Rate Limit Handling - Older Claude queue retry behavior
- Main README - Project overview
Part of the cuti project - Apache 2.0 License