AI agent development container with Claude Code, Codex, RTK, mise, APM, entire, and git-wt pre-installed.
-
macOS + Colima running as the Docker backend. The shell launcher calls
colima ssh/colima statusto resolve the SSH agent socket and to check readiness, so Docker Desktop or other backends are not supported out of the box. -
Colima should use the following config:
vmType: vz mountType: virtiofs forwardAgent: true
If
mountTypeis left onsshfs,entirecan hang on the mounted workspace.
- Start from your current project directory with
agent. - Add
agent --dockeronly when the container must control host Docker. - Container-only state lives under
~/.agent-stack, separate from host dotfiles.
Source the plugin in your ~/.zshrc:
source /path/to/agent-stack/agent-stack.plugin.zshIf you manage shell plugins with sheldon, add it to ~/.config/sheldon/plugins.toml instead:
[plugins.agent-stack]
github = "Syati/agent-stack"
use = ["agent-stack.plugin.zsh"]Then start a container from the current directory:
agentagentbind-mounts the current directory at the same absolute path on the host and sets-w "$(pwd)"so the container starts there.- This is intentional: when
agent --dockerexposes the host Docker socket,docker runanddocker buildexecuted inside the container still have their bind-mount paths and build contexts resolved by the host Docker daemon. - If the in-container path differs from the host path, nested Docker commands can target a non-existent path or the wrong build context.
Run a tool directly instead of starting an interactive shell when needed:
agent codex
agent claude
agent zsh -lc 'uname -a'agent --dockermounts/var/run/docker.sockso the container can control host Docker.- By default, the host Docker socket is not mounted. Use
agent --dockeronly when needed. - The launcher resolves
SSH_AUTH_SOCKfrom inside the Colima VM viacolima sshand mounts the forwarded socket into the container. - Multiple instances can run in parallel. If several agents work on the same repo, use git-wt worktrees to avoid conflicts.
~/.agent-stackstores container-only settings and auth state.~/.agent-stack/.codexis used asCODEX_HOME.~/.agent-stack/.claudeis used asCLAUDE_CONFIG_DIR.~/.agent-stack/.misestores globalmiseconfig and state.agent-mise-datakeeps installedmisetool payloads under/home/agent/.local/share/miseacross container recreations.- Host
~/.gitconfigis mounted read-only sogit commitandgit pushwork inside the container.
On first use, the plugin automatically creates these paths:
~/.agent-stack/.env
~/.agent-stack/.claude
~/.agent-stack/.codex
~/.agent-stack/.chrome-agent
~/.agent-stack/.mise/state
~/.agent-stack/.sheldon/plugins.toml
Authenticate once inside the container to populate them:
- Codex:
codex login --device-auth - Claude Code: run
claudeand complete the normal interactive login flow
With preinstalled sheldon, optional shell plugins can be added in ~/.agent-stack/.sheldon/plugins.toml. For example:
shell = "zsh"
[plugins.entire-fzf]
github = "Syati/entire-fzf"For anything else (aliases, env vars, arbitrary shell config), drop a ~/.agent-stack/.zshrc.local and/or ~/.agent-stack/.bashrc.local — they're sourced automatically at the end of .zshrc / .bashrc if present.
Runs as non-root user agent (home: /home/agent, shell: zsh). The image default working directory is /workspace, but the agent launcher overrides it at runtime to the current directory ($(pwd)).
| 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 |
| sheldon | Plugin manager for user-managed shell extensions |
| gh | GitHub CLI |
| ripgrep | Fast grep (auto-used by RTK) |
| agent-browser | Browser automation for AI agents |
| build-essential | C/C++ compiler toolchain |
~/.agent-stack/.env is optional. Use it only when you want to pass extra environment variables through the shell launcher, for example:
GH_TOKEN=op://Private/github-pat/credential
CHROME_REMOTE_PORT=9222
AGENT_TCP_BRIDGES=127.0.0.1:64342->host.docker.internal:64342
When 1Password CLI (op) is available, op:// references are resolved via op inject automatically. Without op, the file is passed as-is.
AGENT_TCP_BRIDGES is optional. It starts one or more TCP bridges inside the container before your command runs. Use a comma-separated list of listen_host:listen_port->target_host:target_port entries when a host-side service only accepts requests that preserve localhost on the client side.
For example, this lets container-side Codex keep using http://127.0.0.1:64342/stream for RubyMine MCP while forwarding the traffic to the host:
AGENT_TCP_BRIDGES=127.0.0.1:64342->host.docker.internal:64342Multiple bridges can be defined with commas:
AGENT_TCP_BRIDGES=127.0.0.1:64342->host.docker.internal:64342,127.0.0.1:9223->host.docker.internal:9223agent sets these paths explicitly inside the container:
CODEX_HOME=/home/agent/.agent-stack/.codex
CLAUDE_CONFIG_DIR=/home/agent/.agent-stack/.claude
MISE_GLOBAL_CONFIG_FILE=/home/agent/.agent-stack/.mise/config.toml
MISE_STATE_DIR=/home/agent/.agent-stack/.mise/stateThis keeps container auth and settings separate from host-side dotfiles while making mise use -g ... persistent in the agent stack. The installed tool payloads still live in the named Docker volume mounted at /home/agent/.local/share/mise. On first launch, authenticate inside the container once to populate those directories. For Codex, use codex login --device-auth. For Claude Code, run claude and complete the normal interactive login flow.
git clone https://github.com/Syati/agent-stack.git
cd agent-stack
make build
AGENT_STACK_IMAGE=agent-stack:local agentNeed a different tag or Dockerfile? Override the Make variables, for example make build IMAGE=agent-stack:dev or make build DOCKERFILE=docker/Dockerfile.
agent-browser is pre-installed in the container. Start Chrome on the host with remote debugging, then connect from the container.
Host side (agent 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