AI agent development container with Claude Code, Codex, RTK, mise, APM, entire, and git-wt pre-installed.
Add to your project's compose.yml:
services:
agent:
image: ghcr.io/syati/agent-stack:latest
volumes:
- .:/workspace
- /var/run/docker.sock:/var/run/docker.sock # host Docker access
- ${HOME}/.gitconfig:/home/agent/.gitconfig:ro # git config
- ${HOME}/.agent-stack:/home/agent/.agent-stack
- mise-data:/home/agent/.local/share/mise
env_file:
- .env
environment:
CODEX_HOME: /home/agent/.agent-stack/.codex
CLAUDE_CONFIG_DIR: /home/agent/.agent-stack/.claude
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
mise-data:- Docker socket: allows the container to control host Docker (
docker compose run,docker exec, etc.). Remove if not needed. - Bind mount (
~/.agent-stack): persists container-only agent settings on the host.Codexuses~/.agent-stack/.codex,Claudeuses~/.agent-stack/.claude, so they stay separated from host-side~/.codexand~/.claude. - Named volume (
mise-data): persists mise-installed runtimes across container recreations. Cleared bydocker compose down -v. - gitconfig: mounts host git config (read-only) so
git commitandgit pushwork inside the container.
Start the container and connect:
docker compose up -d
docker compose exec -it agent bashUse docker compose exec (not docker attach). Each exec spawns an independent process, so multiple agents can run in parallel without stdin conflicts.
With sheldon, add to ~/.config/sheldon/plugins.toml:
[plugins.agent-stack]
github = "Syati/agent-stack"Or source the plugin directly in your ~/.zshrc:
source /path/to/agent-stack/agent-stack.plugin.zshOn first use, the plugin automatically creates ~/.agent-stack/.env, ~/.agent-stack/.claude, ~/.agent-stack/.codex, and ~/.agent-stack/.chrome-agent.
Multiple instances can run in parallel — each agent call creates a separate container. Use git-wt worktrees to avoid file conflicts when multiple agents work on the same repo.
Runs as non-root user agent (home: /home/agent, shell: zsh). Working directory is /workspace.
| Tool | Description |
|---|---|
| Claude Code | Anthropic's AI coding CLI |
| Codex | OpenAI's AI coding CLI |
| RTK | Token-optimized CLI proxy (60-90% token savings) |
| mise | Dev tool version manager |
| APM | Agent Package Manager for MCP/skills |
| entire | AI session capture for git |
| git-wt | Simplified git worktree management |
| gh | GitHub CLI |
| ripgrep | Fast grep (auto-used by RTK) |
| agent-browser | Browser automation for AI agents |
| build-essential | C/C++ compiler toolchain |
Create ~/.agent-stack/.env (used by the shell function):
ANTHROPIC_API_KEY=op://Private/anthropic/credential
OPENAI_API_KEY=op://Private/openai/credential
GH_TOKEN=op://Private/github-pat/credential
If you already have ~/.agent-stack.env, move its contents into ~/.agent-stack/.env.
When 1Password CLI (op) is available, op:// references are resolved via op inject automatically. Without op, the file is passed as-is.
agent sets these paths explicitly inside the container:
CODEX_HOME=/home/agent/.agent-stack/.codex
CLAUDE_CONFIG_DIR=/home/agent/.agent-stack/.claudeThis keeps container auth and settings separate from host-side ~/.codex and ~/.claude. On first launch, authenticate codex and claude once inside the container to populate the new directories.
See compose.yml for a working example.
git clone https://github.com/Syati/agent-stack.git
cd agent-stack
cp .env.example .env
# edit .env with your keys
make build # build image
make dev # start container
make shell # open shell
make claude # run Claude Code
make codex # run Codex
make update-versions # update tool versions in Dockerfile
make clean # stop and removeagent-browser is pre-installed in the container. Start Chrome on the host with remote debugging, then connect from the container.
Host side (agent chrome, make chrome, or manually):
agent chromeContainer side (run chrome-connect to resolve WebSocket URL and connect):
chrome-connectagent chrome reads CHROME_REMOTE_PORT from ~/.agent-stack/.env and defaults to 9222. Its Chrome profile is stored in ~/.agent-stack/.chrome-agent, so the browser state also stays scoped to agent-stack. The plugin launcher is currently macOS-only because it uses the standard /Applications/Google Chrome.app/... path.
MIT