diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..5faae45 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,20 @@ +## Summary + +- what changed +- why it changed + +## Verification + +- [ ] `./deva.sh --help` +- [ ] `./scripts/version-check.sh` +- [ ] relevant smoke path ran + +## Risk + +- auth / mounts +- installer / release +- docs / site + +## Issues + +Closes # diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccf3022..583cc51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,38 @@ jobs: chmod +x scripts/version-check.sh ./scripts/version-check.sh + smoke: + name: Installer Smoke Test + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Install and launch each agent without a TTY + shell: bash + run: | + set -euo pipefail + export HOME="$(mktemp -d)" + export PATH="$HOME/.local/bin:$PATH" + export DEVA_INSTALL_BASE_URL="file://$PWD" + export DEVA_NO_DOCKER=1 + + bash ./install.sh + + deva.sh claude -Q -- --version + deva.sh codex -Q -- --version + deva.sh gemini -Q -- --version + docs: name: Docs Build runs-on: ubuntu-latest diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 544586d..14caa3a 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -70,8 +70,8 @@ jobs: prompt: ${{ steps.prepare_claude_prompt.outputs.prompt }} claude_args: | --model claude-sonnet-4-20250514 - --system-prompt "Security-focused systems engineer for Claude Code YOLO. Core expertise: container security, auth vectors, shell injection, privilege boundaries. Bark when necessary. PR reviews: paranoia to surface vulnerabilities before ship. Issues/comments: collaborative systems help. Philosophy: minimal comments; code + --help are docs; Linux-style bluntness; fail fast and loud; security first; test all auth methods; follow @workflows/ templates for git/gh ops." - --allowedTools Bash(gh:*),Bash(git:*),Bash(docker:*),Bash(make:*),Bash(./claude.sh:*),Bash(./claudeb.sh:*),Bash(shellcheck:*),mcp__barkme__notify + --system-prompt "Security-focused systems engineer for deva. Core expertise: container security, auth vectors, shell injection, privilege boundaries, and multi-agent launcher behavior across Codex, Claude Code, and Gemini. Bark when necessary. PR reviews: paranoia to surface vulnerabilities before ship. Issues/comments: collaborative systems help. Philosophy: minimal comments; code + --help are docs; Linux-style bluntness; fail fast and loud; security first; test auth and mount paths; follow @workflows/ templates for git/gh ops." + --allowedTools Bash(gh:*),Bash(git:*),Bash(docker:*),Bash(make:*),Bash(./deva.sh:*),Bash(./claude.sh:*),Bash(./claudeb.sh:*),Bash(shellcheck:*),mcp__barkme__notify --mcp-config '{ "mcpServers": { "barkme": { @@ -81,7 +81,7 @@ jobs: "LOG_LEVEL": "debug", "BARK_DEVICES": "${{ secrets.BARK_DEVICES }}", "BARK_SERVER": "${{ secrets.BARK_SERVER }}", - "BARK_GROUP": "Claude YOLO", + "BARK_GROUP": "deva", "BARK_ICON": "https://avatars.githubusercontent.com/in/1452392", "BARK_RETRY": "3", "BARK_ASYNC": "false" diff --git a/AGENTS.md b/AGENTS.md index bd339b2..87f5254 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,258 +1,209 @@ -This file provides guidance to codex, Claude Code when working with code in this repository. +This file provides guidance to coding agents working in this repository. -## Deva Architecture: Container-Based Agent Sandboxing +## What This Repo Is -**CRITICAL DESIGN PATTERN**: Deva purposely runs ALL agents inside Docker containers. The container IS the sandbox. +`deva.sh` is a Docker-based multi-agent launcher for: -- Each agent (claude, codex, gemini) runs in isolated container environment -- Agent internal sandboxes/permission systems are DISABLED: - - claude: `--dangerously-skip-permissions` - - gemini: `--yolo` flag - - codex: equivalent unrestricted mode -- Container provides security boundary instead of agent-level prompts -- Result: No interactive permission prompts while maintaining isolation +- OpenAI Codex +- Claude Code +- Google Gemini CLI -**Why**: Avoids permission fatigue in trusted workspaces while keeping agents containerized for safety. +The container is the sandbox. Agent-level permission theater is not. -## We're following Issue-Based Development (IBD) workflow -1. Before running any Git/GitHub CLI `Bash` command (`git commit`, `gh issue create`, `gh pr create`, etc.), open the corresponding file in @workflows to review required steps. -2. Always apply the exact templates or conventions from the following files: - - @workflows/GITHUB-ISSUE.md → issues - - @workflows/GIT-COMMIT.md → commits - - @workflows/GITHUB-PR.md → pull requests - - @workflows/RELEASE.md → releases -3. Keep one branch per issue; merging the PR must auto-close its linked issue. +That is the core design. Do not "improve" it by moving trust back into +interactive prompt confirmations. -## Release Workflow +## Workflow Rules -For releases, execute the workflow from @workflows/RELEASE.md: +We use issue-based development. -**Steps**: -1. Check prerequisites (clean working dir, CI passing) -2. Detect current version from `claude.sh` VERSION variable -3. Calculate new version using semantic versioning -4. Generate changelog from git log, grouped by commit prefixes -5. Update VERSION in `claude.sh`, add entry to `CHANGELOG.md` -6. Create commit following @workflows/GIT-COMMIT.md conventions -7. Tag and push automatically -8. Verify with git log and tag info +1. Before any Git or GitHub CLI command, read the matching file in + `workflows/`: + - `GITHUB-ISSUE.md` + - `GIT-COMMIT.md` + - `GITHUB-PR.md` + - `RELEASE.md` +2. Keep one branch per issue when practical. +3. PRs should reference and close the relevant issue. +## Release Rules -## PROJECT STRUCTURE +Release workflow lives in `workflows/RELEASE.md`. -``` -claude-code-yolo/ -├── CLAUDE.md # This file - project guidance for Claude -├── README.md # Main documentation -├── CHANGELOG.md # Version history -├── DEV-LOGS.md # Development journal -├── TODO.md # Task tracking -├── install.sh # One-line installer script -├── Makefile # Build automation -├── Dockerfile # Container image definition -├── .dockerignore # Docker build exclusions -├── docker-entrypoint.sh # Container startup script -├── .gitignore # Git exclusions -├── claude.sh # Main wrapper script (local/Docker modes) -├── claude-yolo # Quick YOLO mode wrapper -├── claudeb.sh # Bedrock authentication helper -│ -├── .github/ # GitHub automation -│ ├── ISSUE_TEMPLATE.md # Bug report template -│ └── workflows/ # GitHub Actions -│ ├── ci.yml # Pull request checks -│ ├── release.yml # Release automation -│ └── claude.yml # Claude workflow (unified) -│ -├── workflows/ # Development workflows -│ ├── GIT-COMMIT.md # Commit guidelines -│ ├── GITHUB-ISSUE.md # Issue creation guidelines -│ ├── GITHUB-PR.md # PR creation process -│ └── RELEASE.md # Release workflow -│ -└── references/ # Documentation and research -``` +Current release source of truth: -## PROJECT PURPOSE +- version comes from `deva.sh` +- changelog comes from `CHANGELOG.md` +- images are published by GitHub Actions -**Claude Code YOLO wraps the Claude CLI in Docker to safely enable `--dangerously-skip-permissions` without compromising your local machine.** +Do not use `claude.sh` as the version source. That is old baggage. -### The Problem +## Project Structure -Claude Code CLI is excellent for code editing and development but has friction points: +```text +deva/ +├── deva.sh +├── claude.sh +├── claude-yolo +├── agents/ +├── Dockerfile +├── Dockerfile.rust +├── docker-entrypoint.sh +├── install.sh +├── .deva.example +├── examples/ +├── docs/ +├── .github/workflows/ +├── workflows/ +├── CHANGELOG.md +├── DEV-LOGS.md +└── AGENTS.md +``` -1. **Cost Management**: Claude Pro/Max ($20-200/month) has rate limits. When exhausted, we switch to expensive API usage. -2. **Permission Prompts**: Claude's security design constantly asks for permission to edit files or access web. Annoying in trusted workspaces. -3. **Root Restriction**: Claude CLI refuses `--dangerously-skip-permissions` when running as root/sudo for security. +Important current roles: -### Why YOLO Mode +- `deva.sh`: primary entrypoint and container lifecycle manager +- `agents/*.sh`: agent-specific auth and command wiring +- `install.sh`: one-line installer +- `.github/workflows/ci.yml`: lint, docs, and smoke coverage +- `.github/workflows/release.yml`: tagged image + release flow +- `.github/workflows/nightly-images.yml`: scheduled nightly image refresh -We created `claude.sh --yolo` to solve the permission friction: -- Run Claude with `--dangerously-skip-permissions` for full workspace access -- Container provides the safety boundary instead of permission prompts -- No firewall restrictions, full web access within container +Legacy compatibility wrappers still exist: -### Technical Challenges Solved +- `claude.sh` +- `claude-yolo` -1. **Auth Persistence**: Mount `~/.claude` to avoid re-authentication. Claude hardcodes this path with no config option. -2. **Root Bypass**: Create non-root `claude` user to satisfy CLI security while maintaining full container capabilities. -3. **Path Consistency**: Mount identical directory structure so Claude remembers project contexts. -4. **Multi-Auth Support**: Seamless switching between Claude Pro, API keys, AWS Bedrock, Vertex AI, and GitHub Copilot. +They are compatibility shims, not primary branding. -**Result**: Full Claude Code power with container isolation. No local machine risk, no permission fatigue. +## Security Model -## Project Architecture +Deva runs agent CLIs inside Docker and disables their built-in permission +prompts: -Claude Code YOLO is a Docker-based wrapper for Claude Code CLI that provides safe execution with full development capabilities. The project implements a dual-mode architecture: +- Claude: `--dangerously-skip-permissions` +- Gemini: `--yolo` +- Codex: unrestricted mode equivalent -- **Local Mode** (default): Direct execution for speed -- **YOLO Mode** (`--yolo`): Docker containerized execution with `--dangerously-skip-permissions` safely enabled +That is deliberate. -### Core Components +The security boundary is: -- **`claude.sh`**: Main wrapper script that handles argument parsing, authentication selection, and mode switching -- **`claude-yolo`**: Quick wrapper script that runs `claude.sh --yolo` for convenient YOLO mode access -- **`Dockerfile`**: Ubuntu 24.04-based image with full development stack (Python 3.12, Node.js 22, Go 1.22, Rust, AWS CLI, dev tools) -- **`docker-entrypoint.sh`**: Container initialization script with environment reporting -- **Helper Scripts**: `claudeb.sh` (Bedrock authentication and model alias conversion) +- the container +- the exact mounts and env vars we pass into it -### Authentication System +So the real risks are the host bridges we expose: -The wrapper supports 5 authentication methods with explicit control: -1. Claude App OAuth (`--claude`, `-c`) - Default, uses `~/.claude` -2. Anthropic API Key (`--api-key`, `-a`) - Direct API with model alias conversion -3. AWS Bedrock (`--bedrock`, `-b`) - Uses AWS credentials with model ARN mapping -4. Google Vertex AI (`--vertex`, `-v`) - Uses gcloud credentials -5. GitHub Copilot (`--copilot`) - Uses GitHub token via copilot-api proxy +- mounted workspace paths +- mounted auth files +- `/var/run/docker.sock` +- `--host-net` +- tmux bridge tooling -Model aliases are automatically converted to appropriate formats (API model names vs Bedrock ARNs) based on authentication method. +Do not document or implement this as if the agent sandbox is protecting the +host. It is not. -### Safety Features +## Auth And Config Model -**Dangerous Directory Detection**: The script prevents accidental execution in sensitive directories: -- Detects when running in home directory, system directories (`/`, `/etc`, `/usr`), or other critical paths -- Shows a clear warning about Claude's full access to the directory -- Requires explicit confirmation (`yes`) to proceed -- Protects users from accidentally giving Claude access to all personal files +Deva supports multiple auth modes per agent. -**Docker Socket Warning** (SECURITY-SENSITIVE): -By default, `/var/run/docker.sock` is auto-mounted if present. This grants full Docker API access to the container - effectively equivalent to root on the host. The "container as sandbox" model is weakened when Docker socket is mounted. +Current design: -Implications: -- Agent can start/stop any container on host -- Agent can mount any host path into new containers -- Agent can escape to host via privileged container creation +- default config root is `~/.config/deva` +- per-agent homes live under that root +- `--config-home` isolates auth state +- `-Q` means bare mode: no config loading, no autolink, no host auth mounts +- non-default auth overlays default credential paths instead of moving live + host files around -Mitigations: -- Use `--no-docker` flag to disable auto-mount -- Set `DEVA_NO_DOCKER=1` environment variable -- Only mount when Docker-in-Docker workflows are required +If you touch auth code, verify real `--dry-run` output and one live path. +Auth bugs are usually mount bugs wearing a fake auth moustache. -## Bridges (privileged) +## Container Model -Deva's container IS the sandbox. Bridges punch controlled holes back to the host for specific integrations. Each bridge has TWO components: host-side and container-side. +Persistent containers are keyed by workspace plus container shape. -| Bridge | Host Command | Container Command | Risk | -|--------|--------------|-------------------|------| -| Docker | (auto-mount `/var/run/docker.sock`) | `docker ...` | Root-equivalent on host | -| tmux | `deva-bridge-tmux-host` | `deva-bridge-tmux` | Host command execution | +Shape includes things like: -**tmux bridge**: Connect container tmux client to host tmux server. -- Problem: Unix socket mount fails across macOS<->Linux kernel boundary -- Solution: socat TCP proxy (host) + socat Unix socket (container) -- Security: Container gains full tmux control (send-keys, run-shell, scrollback) +- selected agent +- extra volumes +- explicit config home +- auth mode -Usage: -```bash -# Host (macOS) -./scripts/deva-bridge-tmux-host +So it is not "one container per repo" in the naive sense. Different shapes +must not collide. -# Container -deva-bridge-tmux -tmux -S /tmp/host-tmux.sock list-sessions -``` +For clean repros and CI smoke tests, use: -Environment variables: -- `DEVA_BRIDGE_BIND`: host bind address (default: 127.0.0.1) -- `DEVA_BRIDGE_PORT`: TCP port (default: 41555) -- `DEVA_BRIDGE_HOST`: container's host address (default: host.docker.internal) -- `DEVA_BRIDGE_SOCKET`: host tmux socket path (default: auto-detected) -- `DEVA_BRIDGE_SOCK`: container local socket (default: /tmp/host-tmux.sock) +```bash +deva.sh claude -Q -- --version +deva.sh codex -Q -- --version +deva.sh gemini -Q -- --version +``` -## Docker Architecture Details +Non-interactive launch paths must work without a TTY. -### Volume Mounting Strategy -- Current directory mounted at identical path (preserves absolute paths) -- Authentication directories mounted to `/root/`: `~/.claude`, `~/.aws`, `~/.config/gcloud` -- Project-specific `.claude` settings mounted when present -- Docker socket mounted for Docker-in-Docker scenarios +## Common Checks -### Container Configuration -- Runs as non-root user matching your host UID/GID (for file access) -- Uses `tini` as PID 1 for proper signal handling -- Automatic proxy translation: `localhost` → `host.docker.internal` -- Environment variable pass-through for development tools -- **Automatically adds `--dangerously-skip-permissions` in YOLO mode for full power** -- Copies and sets proper permissions on authentication files (`~/.claude`, `~/.claude.json`) -- **Includes copilot-api proxy for GitHub Copilot integration with automatic lifecycle management** +Run these before claiming things work: -### GitHub Copilot Integration +```bash +./deva.sh --help +./deva.sh --version +./claude-yolo --help +./scripts/version-check.sh +``` -**Cost Advantage**: GitHub Copilot Pro ($10/month) vs Claude Pro ($20/month) - significant savings for teams. +If you changed container launch, mounts, auth, or the installer, also run: -**Available Models**: Access to Claude Sonnet 4, Opus 4.1, GPT-5, and other models through GitHub Copilot subscriptions. +```bash +./deva.sh claude --debug --dry-run +./deva.sh claude -Q -- --version +./deva.sh codex -Q -- --version +./deva.sh gemini -Q -- --version +``` -**Implementation**: Uses copilot-api reverse proxy to expose GitHub Copilot as Anthropic-compatible API endpoints. +If you changed docs site plumbing, also run: -**Usage**: ```bash -# Requires GitHub token (GH_TOKEN or GITHUB_TOKEN) -export GH_TOKEN=$(gh auth token) +mkdocs build --strict +``` -# Local mode (auto-installs copilot-api if needed) -claude.sh --copilot -p "Implement OAuth login" +## Bridges -# Docker mode (copilot-api pre-installed) -claude.sh --yolo --copilot -p "Implement OAuth login" -``` +Bridges are deliberate holes from container back to host. + +Current ones: + +- Docker socket mount +- tmux bridge + +Treat bridge changes as security-sensitive. They change the real trust +boundary, not some fake marketing boundary. -**Local Mode**: Automatically detects and installs Node.js dependencies (npm install -g copilot-api@latest). -**Security**: Proxy lifecycle managed automatically, cleanup on exit, container isolation in Docker mode. +## Documentation Rules -## Key Implementation Patterns +Public copy should say `deva.sh` first. -### Authentication File Handling -Claude requires both `~/.claude/` directory AND `~/.claude.json` file - both must be mounted for OAuth to work properly. +Allowed: -### Proxy Translation -Docker container automatically translates `127.0.0.1` and `localhost` to `host.docker.internal` in proxy URLs for container-to-host communication. +- mention `claude.sh` / `claude-yolo` as compatibility wrappers +- historical notes in changelog/dev logs -### Model Alias System -- Bedrock: Converts aliases to full ARNs (`sonnet-4` → `arn:aws:bedrock:us-west-2:123456789012:inference-profile/us.anthropic.claude-sonnet-4-20250514-v1:0`) -- API: Converts aliases to model names (`sonnet-4` → `claude-sonnet-4-20250514`) -- Supports all current Claude models plus DeepSeek R1 +Not allowed: -### Error Handling -- Missing executables trigger fallback checks and helpful error messages -- Authentication issues can be debugged by running with `--trace` flag -- Docker image auto-builds when missing +- presenting the project as Claude-only +- using `claude.sh` as the primary interface in current docs +- leaving stale release or workflow prompts centered on old naming -## Common Issues Solutions +## Issue Hygiene -* "Invalid API key" - Mount both `~/.claude` and `~/.claude.json` -* Proxy not working - Localhost auto-translates to `host.docker.internal` -* Permission denied - Container copies auth files with proper permissions -* Claude refuses dangerous permissions on root user - YOLO mode runs as non-root + adds `--dangerously-skip-permissions` -* Running in home directory - Script warns and requires confirmation, always cd to project first -* "GH_TOKEN not set" (copilot mode) - Set GitHub token with `export GH_TOKEN=$(gh auth token)` -* "Node.js not found" (copilot local mode) - Install Node.js or use Docker mode `--yolo --copilot` -* "Failed to install copilot-api" - Check npm permissions or use Docker mode +Do not use the issue queue as a mirror of every upstream vendor changelog. -## Development Tools Included +If upstream version tracking is automated, close the old noise and keep the +real issue queue for: -**Languages**: Python 3.12, Node.js 22 (via NodeSource), Go 1.22, Rust -**Build Tools**: make, cmake, gcc, g++, clang -**CLI Tools**: git, docker, aws, jq, ripgrep, fzf, bat, eza, delta -**Editors**: vim, neovim, nano -**Shell**: zsh with oh-my-zsh and plugins -**Claude**: claude, claude-trace, copilot-api pre-installed +- bugs in deva +- missing features in deva +- docs gaps in deva +- concrete release/process work in deva diff --git a/Makefile b/Makefile index d02c27a..a171b1e 100644 --- a/Makefile +++ b/Makefile @@ -278,15 +278,15 @@ version-check: .PHONY: release-patch release-patch: - @./claude-yolo "Execute release workflow from @workflows/RELEASE.md for a **patch** release" + @./deva.sh claude -Q -- -p "Execute release workflow from @workflows/RELEASE.md for a **patch** release" .PHONY: release-minor release-minor: - @./claude-yolo "Execute release workflow from @workflows/RELEASE.md for a **minor** release" + @./deva.sh claude -Q -- -p "Execute release workflow from @workflows/RELEASE.md for a **minor** release" .PHONY: release-major release-major: - @./claude-yolo "Execute release workflow from @workflows/RELEASE.md for a **major** release" + @./deva.sh claude -Q -- -p "Execute release workflow from @workflows/RELEASE.md for a **major** release" .PHONY: help help: diff --git a/README.md b/README.md index 57b2b8e..58a3f13 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ [![Release](https://img.shields.io/github/v/release/thevibeworks/deva?sort=semver)](https://github.com/thevibeworks/deva/releases) [![License](https://img.shields.io/github/license/thevibeworks/deva)](LICENSE) [![Container](https://img.shields.io/badge/ghcr.io-thevibeworks%2Fdeva-blue)](https://github.com/thevibeworks/deva/pkgs/container/deva) -[![Agents](https://img.shields.io/badge/agents-claude%20%7C%20codex%20%7C%20gemini-222222)](#what-this-is) +[![Agents](https://img.shields.io/badge/agents-codex%20%7C%20claude%20%7C%20gemini-222222)](#what-this-is) -Run Claude Code, Codex, and Gemini inside Docker without pretending the agent's own sandbox is the thing keeping you safe. +Run Codex, Claude Code, and Gemini inside Docker without pretending the agent's own sandbox is the thing keeping you safe. The container is the sandbox. Explicit mounts are the contract. Persistent project containers keep the workflow fast instead of rebuilding the same state every run. @@ -15,7 +15,7 @@ This repo is the source of truth for `deva.sh`. ## What This Is -- a Docker-based launcher for Claude, Codex, and Gemini +- a Docker-based launcher for Codex, Claude, and Gemini - one warm default container shape per project by default - explicit mount and env wiring instead of mystery behavior - per-agent config homes under `~/.config/deva/` @@ -42,7 +42,7 @@ Compatibility wrappers still exist: curl -fsSL https://raw.githubusercontent.com/thevibeworks/deva/main/install.sh | bash cd ~/work/my-project -deva.sh claude +deva.sh codex ``` Then inspect the container if you want: @@ -53,7 +53,7 @@ deva.sh ps deva.sh stop ``` -If you already use Claude, Codex, or Gemini locally, deva will auto-link those auth homes into `~/.config/deva/` by default. If not, first run will ask you to authenticate inside the container. +If you already use Codex, Claude, or Gemini locally, deva will auto-link those auth homes into `~/.config/deva/` by default. If not, first run will ask you to authenticate inside the container. ## Docs @@ -77,6 +77,10 @@ Project policy and OSS housekeeping: - [MIT License](LICENSE) - [Live Docs](https://docs.deva.sh) +Examples: + +- [Examples](examples/README.md) + Deep research note: - [UID/GID Handling Research](docs/UID-GID-HANDLING-RESEARCH.md) diff --git a/TODO.md b/TODO.md index f57c2bf..0fd7fd1 100644 --- a/TODO.md +++ b/TODO.md @@ -1,26 +1,14 @@ -# Claude Code YOLO - TODO List +# deva.sh TODO -## High Priority -- [ ] Issue #1: Fix --trace not passing --dangerously-skip-permissions -- [ ] Issue #4: Add container shortcuts to claude-yolo -- [ ] Issue #3: Consistent auth mounting for dev tools -- [ ] Issue #2: Flexible dev tool installation +## Current Priorities -## Medium Priority -- [ ] Refactor claude.sh (570+ LOC) -- [ ] GitHub Actions CI/CD pipeline -- [ ] Dockerfile cleanup +- [ ] Replace the abstract `deva.sh` landing page with a docs-first homepage +- [ ] Sweep remaining live legacy naming from prompts, scripts, and docs +- [ ] Keep CI honest with real installer and non-TTY agent smoke coverage +- [ ] Move upstream vendor chatter out of the main issue queue ---- +## Ongoing Technical Debt -## Completed Recently ✅ -- [X] **Fix Non-Root Execution Bug** 🐛 -- [X] Fix `--dangerously-skip-permissions` injection for ALL claude invocations (not just `$1 == "claude"`) -- [X] Implement credential symlink strategy instead of copying -- [X] Resolve auth mounting inconsistency (`/root/` vs `/home/claude/`) -- [x] Fix credential sync - Use permission + symlink approach instead of copying -- [x] Fix --dangerously-skip-permissions consistency in non-root mode -- [x] Add Docker socket security control via CCYOLO_DOCKER_SOCKET env var -- [x] Gate Docker socket mount behind `CCYOLO_DOCKER_SOCKET=true` env var (default: off) -- [x] Make risky mounts opt-in, not default -- [x] `check_dangerous_directory` +- [ ] Keep shrinking auth and mount edge cases +- [ ] Keep the release workflow aligned with the actual image pipeline +- [ ] Trim compatibility-wrapper confusion without breaking existing users diff --git a/deva.sh b/deva.sh index 1398193..eda67f5 100755 --- a/deva.sh +++ b/deva.sh @@ -26,6 +26,7 @@ CONFIG_ROOT="" USER_VOLUMES=() USER_ENVS=() EXTRA_DOCKER_ARGS=() +DOCKER_TERMINAL_ARGS=() CONFIG_HOME="" CONFIG_HOME_AUTO=false CONFIG_HOME_FROM_CLI=false @@ -44,6 +45,12 @@ GLOBAL_MODE=false DEBUG_MODE=false DRY_RUN=false +if [ -t 0 ] && [ -t 1 ]; then + DOCKER_TERMINAL_ARGS=(-it) +elif [ ! -t 0 ]; then + DOCKER_TERMINAL_ARGS=(-i) +fi + usage() { cat <<'USAGE' deva.sh - Docker-based multi-agent launcher (Claude, Codex, Gemini) @@ -643,7 +650,7 @@ prepare_base_docker_args() { if [ "$EPHEMERAL_MODE" = true ]; then container_name="${DEVA_CONTAINER_PREFIX}-${slug}${suffix}-${ACTIVE_AGENT}-$$" - DOCKER_ARGS=(run --rm -it) + DOCKER_ARGS=(run --rm "${DOCKER_TERMINAL_ARGS[@]}") else container_name="${DEVA_CONTAINER_PREFIX}-${slug}${suffix}" DOCKER_ARGS=(run -d) @@ -2402,7 +2409,7 @@ if [ "$DEBUG_MODE" = true ]; then echo "" >&2 if [ "$EPHEMERAL_MODE" = false ]; then echo "docker run -d $(mask_secrets_in_args "${DOCKER_ARGS[@]:2}") tail -f /dev/null" >&2 - echo "docker exec -it $CONTAINER_NAME /usr/local/bin/docker-entrypoint.sh ${AGENT_COMMAND[*]}" >&2 + echo "docker exec ${DOCKER_TERMINAL_ARGS[*]} $CONTAINER_NAME /usr/local/bin/docker-entrypoint.sh ${AGENT_COMMAND[*]}" >&2 else echo "docker $(mask_secrets_in_args "${DOCKER_ARGS[@]}") ${AGENT_COMMAND[*]}" >&2 fi @@ -2470,7 +2477,7 @@ if [ "$EPHEMERAL_MODE" = false ]; then update_session_file fi - exec docker exec -it "$CONTAINER_NAME" /usr/local/bin/docker-entrypoint.sh "${AGENT_COMMAND[@]}" + exec docker exec "${DOCKER_TERMINAL_ARGS[@]}" "$CONTAINER_NAME" /usr/local/bin/docker-entrypoint.sh "${AGENT_COMMAND[@]}" else echo "Launching ${ACTIVE_AGENT} (ephemeral mode) via ${DEVA_DOCKER_IMAGE}:${DEVA_DOCKER_TAG}" write_session_file diff --git a/docs/index.md b/docs/index.md index 81482e5..5c3268e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ # deva.sh -Run Claude Code, Codex, and Gemini inside Docker without pretending the +Run Codex, Claude Code, and Gemini inside Docker without pretending the agent's own sandbox is the thing keeping you safe. The container is the sandbox. Explicit mounts are the contract. @@ -22,7 +22,7 @@ If you want the internals instead of vague hand-waving: ## What This Is -- a Docker-based launcher for Claude, Codex, and Gemini +- a Docker-based launcher for Codex, Claude, and Gemini - one warm default container shape per project by default - explicit mount and env wiring instead of mystery behavior - per-agent config homes under `~/.config/deva/` @@ -41,7 +41,7 @@ If you want the internals instead of vague hand-waving: curl -fsSL https://raw.githubusercontent.com/thevibeworks/deva/main/install.sh | bash cd ~/work/my-project -deva.sh claude +deva.sh codex ``` Then inspect the container if you want: diff --git a/docs/quick-start.md b/docs/quick-start.md index b0c834d..7562bfc 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -21,8 +21,8 @@ curl -fsSL https://raw.githubusercontent.com/thevibeworks/deva/main/install.sh | That installs: - `deva.sh` -- `claude.sh` -- `claude-yolo` +- `claude.sh` (legacy compatibility wrapper) +- `claude-yolo` (legacy compatibility wrapper) - `agents/claude.sh` - `agents/codex.sh` - `agents/gemini.sh` @@ -34,7 +34,7 @@ It also pulls `ghcr.io/thevibeworks/deva:latest`, with Docker Hub as fallback. ```bash cd ~/work/my-project -deva.sh claude +deva.sh codex ``` By default, deva: @@ -71,7 +71,7 @@ deva.sh rm Same project, same default container shape: ```bash -deva.sh codex +deva.sh claude deva.sh gemini ``` @@ -81,6 +81,13 @@ If you change mounts, explicit config-home, or auth mode, deva will split into a ## Quick Auth Examples +Codex with OpenAI API key: + +```bash +export OPENAI_API_KEY=sk-... +deva.sh codex --auth-with api-key +``` + Claude with a direct Anthropic-style key or token: ```bash @@ -96,13 +103,6 @@ export ANTHROPIC_AUTH_TOKEN=token deva.sh claude --auth-with api-key ``` -Codex with OpenAI API key: - -```bash -export OPENAI_API_KEY=sk-... -deva.sh codex --auth-with api-key -``` - Gemini with API key: ```bash diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..6941bdc --- /dev/null +++ b/examples/README.md @@ -0,0 +1,16 @@ +# Examples + +Three examples. No framework cosplay. + +## `basic/.deva` + +Default per-project settings with a couple of common mounts. + +## `rust/.deva` + +Use the rust profile and keep a couple of useful developer env vars. + +## `isolated-auth/.deva` + +Show how to keep auth state separate instead of reusing your default home for +everything. diff --git a/examples/basic/.deva b/examples/basic/.deva new file mode 100644 index 0000000..9c02035 --- /dev/null +++ b/examples/basic/.deva @@ -0,0 +1,3 @@ +VOLUME=$HOME/.ssh:/home/deva/.ssh:ro +VOLUME=$HOME/.config/git:/home/deva/.config/git:ro +ENV=EDITOR=nvim diff --git a/examples/isolated-auth/.deva b/examples/isolated-auth/.deva new file mode 100644 index 0000000..109b842 --- /dev/null +++ b/examples/isolated-auth/.deva @@ -0,0 +1,2 @@ +ENV=DEVA_NO_AUTOLINK=1 +ENV=DEVA_NO_DOCKER=1 diff --git a/examples/rust/.deva b/examples/rust/.deva new file mode 100644 index 0000000..12fc05d --- /dev/null +++ b/examples/rust/.deva @@ -0,0 +1,3 @@ +PROFILE=rust +ENV=RUST_BACKTRACE=1 +ENV=CARGO_TERM_COLOR=always diff --git a/install.sh b/install.sh index f906456..4e2b5de 100644 --- a/install.sh +++ b/install.sh @@ -6,7 +6,7 @@ LEGACY_WRAPPER="claude.sh" YOLO_WRAPPER="claude-yolo" DOCKER_IMAGE="ghcr.io/thevibeworks/deva:latest" DOCKER_IMAGE_FALLBACK="thevibeworks/deva:latest" -GITHUB_RAW="https://raw.githubusercontent.com/thevibeworks/deva/main" +INSTALL_BASE_URL="${DEVA_INSTALL_BASE_URL:-https://raw.githubusercontent.com/thevibeworks/deva/main}" agent_files=( "claude.sh" @@ -39,7 +39,7 @@ echo "Installing to: $INSTALL_DIR" download() { local path="$1" local dest="$2" - curl -fsSL "$GITHUB_RAW/$path" -o "$dest" + curl -fsSL "$INSTALL_BASE_URL/$path" -o "$dest" chmod +x "$dest" } @@ -81,8 +81,8 @@ echo "Quick start:" echo " 1. Make sure Docker is running" echo " 2. cd into a project" echo " 3. Run one of:" -echo " deva.sh claude" -echo " deva.sh codex -- --help" +echo " deva.sh codex" +echo " deva.sh claude -- --help" echo " deva.sh gemini -- --help" echo " deva.sh shell" echo ""