REBRANDED: Claude Code YOLO → deva.sh Multi-Agent Wrapper We've evolved from a Claude-specific wrapper into a unified development environment supporting Claude Code, OpenAI Codex, and future coding agents.
deva.sh launches Claude Code, Codex, and future coding agents inside a Docker container with full control over mounts, environments, and authentication contexts.
Key Features:
- Advanced Directory Access: Beyond Claude's
--add-dir- mount any directories with precise permissions (-v) - Multiple Config Homes: Isolated auth contexts with
--config-homefor different accounts/orgs - Multi-Auth Support: OAuth, API keys, Bedrock, Vertex, Copilot - all in one wrapper
- Safe Dangerous Mode: Full permissions inside containers, zero host risk
What Changed: claude.sh → deva.sh as the primary interface. Your existing claude-yolo commands still work via compatibility shims.
# install the CLI wrapper
curl -fsSL https://raw.githubusercontent.com/thevibeworks/claude-code-yolo/main/install.sh | bash
# run from a project directory
cd ~/wrk/my-project
deva.sh # Claude by default
deva.sh codex -- --help # Codex CLI passthroughclaude-yolo simply calls deva.sh claude for backwards compatibility.
Note The older
claude.sh/claudeb.shwrappers are deprecated and now print a reminder before forwarding todeva.sh.
Supported Agents:
- Claude Code (
deva.sh claude) - Anthropic's Claude with Code capabilities, auto-adds--dangerously-skip-permissions - OpenAI Codex (
deva.sh codex) - OpenAI's Codex CLI, auto-adds--dangerously-bypass-approvals-and-sandbox
Agent-Specific Features:
- Claude: OAuth/API key auth, mounts
~/.claude, project-specific.claudeconfigs - Codex: OAuth protection (strips conflicting
OPENAI_*env vars), exposes port 1455, mounts~/.codex
Shared Infrastructure:
- Project-scoped containers (
deva-<agent>-<project>-<pid>) - Unified config system (
.deva*files) - Container management (
--ps,--inspect,shell)
- Always
cdinto the project root before launching; deva.sh mounts the working directory recursively. - Never aim
--config-homeat your real$HOME; use a dedicated auth directory. - Use
deva.sh --psto confirm which containers are running before attaching.
Claude's --add-dir vs deva.sh's Military-Grade Control:
# Claude built-in: Dangerous direct filesystem access
claude --add-dir ~/projects/backend --add-dir ~/shared-libs
# ❌ Claude can read/write EVERYTHING in those directories
# ❌ No permission control
# ❌ Direct access to your real files
# ❌ Can accidentally modify/delete host files
# deva.sh: Fortress-level security with surgical precision
deva.sh claude \
-v ~/projects/backend:/home/deva/backend:ro \ # READ-ONLY
-v ~/shared-libs:/home/deva/libs:ro \ # READ-ONLY
-v ~/api-keys:/home/deva/secrets:ro \ # READ-ONLY SECRETS
-v /tmp/build-output:/home/deva/output:rw # ONLY writable area
# ✅ Granular per-directory permissions
# ✅ Container isolation = zero host risk
# ✅ Secrets are read-only, impossible to leak
# ✅ Only designated output dir is writableWhy deva.sh's Volume Mounting is Vastly More Secure & Controllable:
- Granular Permissions: Per-directory read-only (
ro) vs read-write (rw) vs no access - Complete Isolation: Claude never touches your real filesystem - only container copies
- Selective Exposure: Choose exactly which files/dirs are visible, hide everything else
- Path Remapping: Control exactly where files appear in container (
/home/deva/projectvs~/my-secret-project) - Zero Host Risk: Even with
--dangerously-skip-permissions, your host filesystem is protected - Credential Sandboxing: Mount secrets read-only, impossible for Claude to modify/leak them
- Audit Trail: Docker logs every file access, unlike native
--add-dir - Performance: Docker volume caching + no host filesystem traversal
Real-World Security Scenarios:
# MAXIMUM SECURITY: Code review with zero risk
deva.sh claude \
-v ~/client-project:/home/deva/code:ro \ # REVIEW ONLY
-v ~/api-keys:/home/deva/keys:ro \ # SECRETS READ-ONLY
-v /tmp/review-output:/home/deva/output:rw # SAFE OUTPUT AREA
# Claude can analyze code but CANNOT modify source or leak secrets
# SURGICAL ACCESS: Database migration with controlled risk
deva.sh claude \
-v ~/migrations:/home/deva/migrations:ro \ # SCRIPTS READ-ONLY
-v ~/.db-config:/home/deva/config:ro \ # CONFIG READ-ONLY
-v /tmp/migration-logs:/home/deva/logs:rw # LOGS ONLY
# Claude can read configs but CANNOT modify production scripts
# CREDENTIAL FORTRESS: API development with bulletproof secrets
deva.sh claude \
-v ~/project/src:/home/deva/src:rw \ # CODE ACCESS
-v ~/.aws:/home/deva/.aws:ro \ # AWS CREDS READ-ONLY
-v ~/api-keys:/home/deva/keys:ro \ # API KEYS READ-ONLY
-v /tmp/build:/home/deva/build:rw # BUILD OUTPUT ONLY
# Impossible for Claude to accidentally commit secrets or modify credentials
# Compare with dangerous --add-dir approach:
# claude --add-dir ~/project --add-dir ~/.aws --add-dir ~/api-keys
# ❌ Claude has FULL WRITE ACCESS to ALL your secrets!XDG Config Home (Per-Agent):
$XDG_CONFIG_HOME defaults to ~/.config
# Default layout (each agent directory under ~/.config/deva mounts into /home/deva):
~/.config/deva/
├── claude/ # Claude-only auth/config
│ ├── .claude/
│ ├── .claude.json
│ ├── .aws/ # optional: Bedrock creds
│ └── .config/gcloud/ # optional: Vertex AI creds
└── codex/ # Codex-only auth/config
└── .codex/ # contains auth.json
# Mount destinations inside container:
# - claude/* → /home/deva/*
# - codex/* → /home/deva/*
# Override per invocation
deva.sh claude -c ~/auth-homes/personal
deva.sh codex -c ~/auth-homes/codex-prod -- -m gpt-5-codex
deva.sh claude -c ~/auth-homes/client-aws --auth-with bedrockNotes:
- By default, we mount all agent homes found under
~/.config/deva/so you can run multiple agents in the same container. - If you pass
-cto a directory that containsclaude/and/orcodex/, we treat it as a DEVA ROOT and mount all agent homes found there. - If
-cpoints to a leaf directory with dotfiles directly (e.g., only.claude*), we mount only that directory.
Migration from legacy dotfiles:
# 1) Create the new XDG tree
mkdir -p ~/.config/deva/{claude,codex}
# 2) Move Claude Code OAuth files
if [ -d ~/.claude ]; then mv ~/.claude ~/.config/deva/claude/.claude; fi
if [ -f ~/.claude.json ]; then mv ~/.claude.json ~/.config/deva/claude/.claude.json; fi
# 3) Move Codex OAuth directory
if [ -d ~/.codex ]; then mv ~/.codex ~/.config/deva/codex/.codex; fi
# 4) Optional: Bedrock / Vertex creds (choose one spot)
# Either keep per-agent:
if [ -d ~/.aws ]; then cp -a ~/.aws ~/.config/deva/claude/.aws; fi
if [ -d ~/.config/gcloud ]; then mkdir -p ~/.config/deva/claude/.config && \
cp -a ~/.config/gcloud ~/.config/deva/claude/.config/; fi
# 5) Verify
tree -a ~/.config/deva | sed -n '1,200p'
# Done. Now just run
deva.sh claude
deva.sh codexSymlink Setup
Prefer links if you don’t want to move files. Docker resolves symlinks at start and binds the target.
# Link specific files/dirs into deva root
ln -s ~/.claude ~/.config/deva/claude/.claude
ln -s ~/.claude.json ~/.config/deva/claude/.claude.json
ln -s ~/.codex ~/.config/deva/codex/.codex
# Or link whole agent directories (cleaner)
ln -s ~/auth-homes/claude ~/.config/deva/claude
ln -s ~/auth-homes/codex ~/.config/deva/codex
# Verify symlinks resolve
ls -l ~/.config/deva/claude
readlink -f ~/.config/deva/claude/.claudeNotes:
- Use absolute paths for links. Relative is fine if it resolves correctly from the link’s parent.
- The symlink’s target must exist, or the container mount will fail.
- Changing the link after the container starts won’t retarget the running bind; restart to pick up changes.
- macOS: ensure the target paths are allowed under Docker Desktop File Sharing.
Auto-linking (default)
- On first run with the default XDG root (
~/.config/deva) and no explicit-c, we auto-link legacy creds if the deva root is missing them:~/.claude→~/.config/deva/claude/.claude~/.claude.json→~/.config/deva/claude/.claude.json~/.codex→~/.config/deva/codex/.codex
- Disable with any of:
- CLI:
--no-autolink - Config: add
AUTOLINK=falseto.deva - Env:
DEVA_NO_AUTOLINK=1(export and run)
- CLI:
Container Management:
# List all running containers for this project
% deva.sh --ps
NAME AGENT STATUS CREATED AT
deva-claude-my-project-12345 claude Up 2 minutes 2025-09-18 18:10:02 +0000 UTC
deva-codex-my-project-67890 codex Up 5 minutes 2025-09-18 18:07:15 +0000 UTC
# Attach to any container (fzf picker if multiple)
deva.sh --inspect
deva.sh shell # alias for --inspectConfig Home Structure (--config-home DIR / -c DIR):
deva.sh mounts entire auth directories into /home/deva, enabling isolated authentication contexts for different accounts, organizations, or projects.
# Organize by account type
~/auth-homes/
├── personal/ # Personal accounts
│ ├── .claude/
│ ├── .claude.json
│ └── .config/gcloud/
├── work-corp/ # Corporate accounts
│ ├── .claude/ # Work Claude Pro
│ ├── .aws/ # AWS Bedrock access
│ └── .codex/ # OpenAI org license
└── client-proj/ # Client-specific
├── .claude/ # Client Claude account
└── .aws/ # Client AWS Bedrock
# Use different auth contexts seamlessly
deva.sh claude -c ~/auth-homes/personal
deva.sh claude -c ~/auth-homes/work-corp --auth-with bedrock
deva.sh codex -c ~/auth-homes/work-corpAuth Protection: When .codex/auth.json is mounted, deva.sh strips conflicting OPENAI_* env vars to ensure OAuth sessions aren't shadowed by stale API credentials.
deva.sh --ps– listdeva-*containers scoped to the current project (includes inferred agent column).deva.sh --inspect/deva.sh shell– attach to a running container (fzfpicker if more than one, otherwise auto-attach).
Container naming pattern: deva-<agent>-<project>-<pid>.
We load configuration in this order (later wins):
$XDG_CONFIG_HOME/deva/.deva$HOME/.deva.deva.deva.local- Legacy
.claude-yolo*files (still honoured for compatibility)
Example .deva file:
VOLUME=~/.ssh:/home/deva/.ssh:ro
VOLUME=~/.gitconfig:/home/deva/.gitconfig:ro
ENV=DEBUG=${DEBUG:-development}
ENV=GH_TOKEN
CONFIG_HOME=~/auth-homes/claude-max
DEFAULT_AGENT=claude
PROFILE=rust # pick a dev profile (same as -p rust)
HOST_NET=falseSupported keys: VOLUME, ENV, CONFIG_HOME, DEFAULT_AGENT, HOST_NET, plus any valid shell variable names you want exported.
All Authentication Methods Supported:
| Agent | Auth Method | Command Example | Auth Context |
|---|---|---|---|
| Claude | OAuth | deva.sh claude -c ~/auth-homes/personal |
.claude/, .claude.json |
| Claude | API Key | deva.sh claude --auth-with api-key |
ANTHROPIC_API_KEY |
| Claude | Bedrock | deva.sh claude -c ~/auth-homes/aws --auth-with bedrock |
.aws/ credentials |
| Claude | Vertex AI | deva.sh claude -c ~/auth-homes/gcp --auth-with vertex |
.config/gcloud/ |
| Claude | Copilot | deva.sh claude --auth-with copilot |
GitHub token via copilot-api |
| Claude | OAuth Token | deva.sh claude -- --auth-with oat -p "task" |
CLAUDE_CODE_OAUTH_TOKEN |
| Codex | OAuth | deva.sh codex -c ~/auth-homes/openai |
.codex/auth.json |
Multi-Org Support Examples:
# Personal dev work
deva.sh claude -c ~/auth-homes/personal
# Corporate Bedrock account
deva.sh claude -c ~/auth-homes/corp-aws --auth-with bedrock
# Client project with their OpenAI license
deva.sh codex -c ~/auth-homes/client-openai
# Quick API key for testing
ANTHROPIC_API_KEY=sk-... deva.sh claude -- --auth-with api-key -p "test this"- Languages: Python 3.12, Node.js 22, Go 1.22, Rust.
- Tooling: git, gh, docker CLI, awscli, bun, uv, ripgrep, shellcheck, claude-trace, codex CLI, etc.
- User: non-root
devawith host UID/GID mirroring. - Networking:
localhost→host.docker.internalrewrites for HTTP/HTTPS/gRPC.
make build # build the deva image locally
make shell # open an interactive shell inside the image
./deva.sh --help # list all optionsSee CHANGELOG.md and DEV-LOGS.md in this directory for history and daily notes.
Select a development image profile (deva flags can appear before or after the agent):
# Use base image (default)
deva.sh claude
# Use rust toolchain image (tries pull, then local Dockerfile.rust if present)
deva.sh -p rust claude
deva.sh claude -p rust # equivalent
# If the tag isn't available locally and pull fails, build it
make build-rust # or: docker build -f Dockerfile.rust -t ghcr.io/thevibeworks/deva:rust .Profiles map to images:
- base →
ghcr.io/thevibeworks/deva:latest - rust →
ghcr.io/thevibeworks/deva:rust(falls back to localDockerfile.rustwhen available)
You can also pin a custom image via env: DEVA_DOCKER_IMAGE, DEVA_DOCKER_TAG.