A reproducible, multi-architecture development environment built for agent-driven software development.
agent-devcontainer combines a ready-to-use container image, a plugin catalog for
Claude Code and OpenCode, project-memory workflows, automated pull-request review,
validation tooling, and reusable scaffolding for adopting the environment in other
repositories.
- Multi-architecture
linux/amd64andlinux/arm64images published through GHCR. - Python development through
uv, plus Bun and Node.js 24. - A supervised nested Docker engine, Buildx, Compose, CMake, Ninja, Git LFS, and GitHub CLI.
- Claude Code and OpenCode preinstalled with the shared
agentdevcatalog. - An optional
self-improveplugin that turns verified corrections into reviewable instructions, published but not enabled by default. - A browser-accessible Xpra desktop with VirtualGL support.
- An optional default-deny egress firewall with repository-controlled allowlisting.
- IWE knowledge-graph workflows from project setup through verified shipment.
- GitHub Actions for validation, formatting, image publishing, and AI review.
- A resumable template workflow for adopting or updating the environment elsewhere.
The repository publishes four images:
ghcr.io/plume-works/agentdev-devcontainer:edge— the Dev Container runtime: tools, the staged catalog, supervised services, and the lifecycle commands.ghcr.io/plume-works/agentdev-deploy:edge— a self-starting single-project OpenCode runtime; see Standalone OpenCode runtime.ghcr.io/plume-works/agentdev-base:edge— the lifecycle-neutral base both runtimes derive from, for custom images that bring their own runtime.ghcr.io/plume-works/ubuntu-ansible:edge— the Ansible-ready base image.
All are built on native runners and merged into multi-architecture manifests. The two
runtimes are built from the same run's agentdev-base digest, and edge moves only
after both pass native acceptance. The agent-desktop and agent-opencode names are no
longer published; /agentdev:template-consume update mode migrates a repository that
still pins agent-desktop.
There are three supported adoption paths, all on the same published runtime.
Point an existing devcontainer at the published image:
The image's metadata supplies the create, start, and attach commands, waitFor, the
privileged mode and /var/lib/docker volume nested Docker needs, and its entrypoint
starts the supervised services. Declare none of them again, add no Docker-in-Docker
Feature, and copy no lifecycle script; the editor attaches only once the required
services are ready.
Pin by tag and digest so the environment never changes silently. Renovate or an equivalent dependency updater can advance the digest when a new image is available; this repository's Renovate configuration provides a working example.
Add tooling in an image that starts FROM ghcr.io/plume-works/agentdev-devcontainer.
Extend the lifecycle with executable hooks in
/etc/agentdev/hooks/{created,started,attached}.d/, which run in name order at the end
of their phase, and override the image's documented environment. Files below
/opt/agentdev-runtime/ belong to the image: the runtime refuses to start when one is
changed, added, or removed. See Dev Container
runtime.
Run /agentdev:template-consume to copy the complete setup or merge selected parts
into an existing repository. The workflow installs the devcontainer descriptor,
agent settings, project tooling, and adaptable GitHub workflows.
It also records the adopted revision so future runs can safely propose upstream
updates.
Template adoption is resumable, preserves settled choices, and never overwrites consumer-authored IWE knowledge. See the template consumption guide for prerequisites and the full-copy and selective-copy workflows.
agentdev-deploy runs one project with no Dev Containers client, Coder, or project
scaffolding. On start it creates its runtime account, prepares the workspace, and
starts the OpenCode server, with nested Docker, an instance-local keyring, Codebase
Memory MCP, and the Xpra desktop around it. Each service is supervised and recovered
within a bounded budget. Claude Code is installed and configured but not started.
Deploying agentdev-deploy walks a new host through the
proxy network, settings, first start, proxy routes, logins, and continuity checks.
The supported deployment is compose.opencode.yml behind an
existing reverse proxy. It publishes no host ports. Every launch adds the digest pin in
opencode-compose-pins.yml, which Renovate advances to
each digest CI promotes to edge; CI promotes only builds that passed native
acceptance. Settings come
from a private copy of
docker/deploy/runtime.env.example:
# First start clones AGENTDEV_REPOSITORY_URL into a persistent workspace volume.
docker compose -p myproject --env-file ~/secrets/myproject.env \
-f compose.opencode.yml -f opencode-compose-pins.yml up -d
# Or mount a host checkout (AGENTDEV_CHECKOUT) at the same in-container path.
docker compose -p myproject --env-file ~/secrets/myproject.env \
-f compose.opencode.yml -f opencode-compose-pins.yml -f compose.opencode.bind.yml up -dThe project name (-p) scopes the instance's volumes. A clone-mode workspace is
cloned once and then reused; the runtime never fetches, pulls, resets, or switches
branches. A bind-mounted worktree also needs its main repository's .git mounted at
the path its .git file names.
OPENCODE_SERVER_PASSWORDis required. The proxy routes to the container on port4096for OpenCode, which enforces the password itself, and on14500for Xpra. Both are served at the root path, WebSockets included.XPRA_PASSWORDis required unlessAGENTDEV_SKIP_XPRAis set; without it the runtime latches failed before starting anything. Xpra checks it on every session, so a peer on the proxy network cannot bypass the proxy. The HTML5 page loads without it and asks for it on connect.AGENTDEV_PROXY_NETWORKnames the proxy's existing Docker network.- Claude Code logins live on the shared
AGENTDEV_AUTH_VOLUMEand reach every project container in the same identity cohort. Each project container logs in to OpenCode once: its logins share a database with its sessions in the instancehomevolume, and survive replacement and upgrade there. Sessions, configuration, keyrings, caches, and Docker engines stay per instance. - GitHub access uses an injected
GH_TOKENorGITHUB_TOKEN, which configures HTTPS Git throughghbefore a private clone. The token is never written to disk.GIT_AUTHOR_*andGIT_COMMITTER_*pass through only when set.
Tools run as agent with passwordless sudo, using numeric IDs from
AGENTDEV_UID/AGENTDEV_GID (default 1000:1000). Every container sharing one auth
volume must use the same IDs; a container with other IDs latches failed rather than
change the volume. A bind-mounted checkout must be writable by those IDs, and its
ownership is never changed.
| Path | Volume | Holds |
|---|---|---|
/workspaces/project |
workspace or the bind |
The checkout |
/home/agent |
home |
Configuration, OpenCode logins, sessions, keyring, CBM |
/uv |
uv |
uv cache, Python installs, project venv |
/var/lib/docker |
docker |
The nested engine, including its image store |
/var/lib/agentdev/auth |
agentdev-opencode-auth |
Shared Claude Code logins |
Back up home, workspace, and the auth volume to keep an instance's state; uv and
docker can be rebuilt. Removing the container keeps every volume. docker compose down -v also deletes the shared auth volume, so remove a single project's state by
name instead, for example docker volume rm myproject_home myproject_uv.
The core path is the root bootstrap (account, volumes, and the opt-in firewall), the session bus and keyring, then the core phase (checkout, the Claude credential links, agent policy), then OpenCode. Everything else is optional work:
- before OpenCode, which reads them when it starts: catalog registration, the Codebase Memory MCP client configuration, and the CBM daemon its MCP client connects to;
- beside OpenCode: Xpra, which neither waits for OpenCode nor delays it;
- after OpenCode: nested Docker and its image prune, uv sync, pre-commit hook registration, and a background index request.
Hook environments build on first use rather than at startup. The index request returns once CBM accepts the job, and the daemon indexes in the background, so a large workspace never delays startup.
The runtime reports one state:
pendingwhile startup or a recovery runs;readywhen everything is up;degradedwhen optional work failed or exhausted its recovery budget. OpenCode keeps serving.failedwhen the bootstrap, a configuration check, the core phase, or a core service failed. The runtime is latched: PID 1 stays up, nothing restarts or reruns, and OpenCode is stopped. Docker's restart policy never reruns preparation.
AGENTDEV_STRICT_STARTUP=true treats any optional failure as a core one. The container
exits nonzero, and Docker restarts it, only when the supervisor's own controller or
supervisord fails.
docker logscarries every service's output. The image healthcheck reports healthy only while a live controller reportsreadyand the core probes pass;pending,degraded, andfailedall report unhealthy.docker exec <container> agentdev-runtime health statusprints the state, each failed or blocked component, the last failure record, and the retry command for each failure.- Every failure writes
~/.local/state/agentdev/last-failure.jsonon thehomevolume: the start's ID, the component, its command and exit status, the last 200 lines of its output, and the time. A later start never deletes it; only a later failure replaces it. A failure before the home volume is checked, such as an invalid setting or a failed bootstrap, goes todocker logsonly. - After fixing the cause,
docker exec <container> agentdev-runtime supervisor retryresumes a latched runtime from the failed component in the same container, andretry <component>reruns one failed optional component. The usual causes are an unwritable checkout, an auth volume from another identity, a missing worktree.git, or a failed project setup. docker exec <container> supervisorctl -c /etc/agentdev/supervisord.conf statuslists the services.docker exec -u agent -it <container> bashgives a shell with the same Docker, keyring, and display environment OpenCode's tools see.- To upgrade, advance the pin and run the same
up -d. The new image reapplies its managed client and catalog configuration and keeps user settings and sessions. AGENTDEV_SKIP_XPRA=1drops the desktop. The egress firewall stays off unlessENABLE_FIREWALL=true, which applies the default-deny policy and then needsAGENTDEV_TRUSTED_PROXY_CIDRS.- Booleans,
ENABLE_FIREWALLincluded, accept1,true,yes, oron, and0,false,no,off, or empty. Counts and deadlines must be positive. Any other value latches a configuration failure naming the variable.
| Area | Included tools and capabilities |
|---|---|
| Python | uv, system Python, and pre-commit |
| JavaScript | Bun, Node.js 24, and Yarn |
| Agents | Claude Code, OpenCode, MCP Inspector, and Codebase Memory MCP |
| Build tooling | build-essential, Kitware CMake, Ninja, and pkg-config |
| Quality | Ruff, ShellCheck, Zizmor, Ansible Lint, Prettier, Hadolint, Gitleaks, and Actionlint |
| GitHub | Git, Git LFS, gh, and a transparent host-token wrapper |
| Containers | Docker CE, Buildx, and Compose |
| Desktop | Xpra with its HTML5 client, VirtualGL, Mesa, and Xvfb |
| Credentials | Headless GNOME Keyring and shared agent authentication |
| Security | An opt-in egress firewall with an allowlist and startup checks |
Python commands run through uv run. Python environments, agent state, Codebase
Memory data, and pre-commit environments persist across container rebuilds and remain
isolated per worktree. Authentication is shared so opening another worktree does not
require signing in again.
Earlier images also shipped Codex. Claude Code and OpenCode are now the only
supported harnesses, so an existing devcontainer's agentdev-codex volume and the
codex/auth.json file on the shared auth volume are no longer used. Nothing removes
them; delete them by hand once you no longer need them:
# A devcontainer's per-instance Codex volume
docker volume rm <project>_agentdev-codex
# The Codex login on the devcontainer's shared auth volume
docker run --rm -v agentdev-agents-auth:/auth busybox rm -rf /auth/codex
# The Codex login on an agentdev-deploy auth volume
docker run --rm -v "${AGENTDEV_AUTH_VOLUME:-agentdev-opencode-auth}":/auth busybox rm -rf /auth/codexThe repository is its own plugin marketplace. Claude Code sees two plugins,
agentdev and self-improve; OpenCode loads agentdev through the bridge plugin
it ships.
The image carries agentdev 5.0.0, a cross-agent plugin with 37 skills and five
agent definitions for Claude Code and OpenCode. It covers:
- Git commits, branch updates, merges, and conflict resolution.
- Pull-request creation, synchronization, review, feedback resolution, and merging.
- GitHub Actions and CodeQL diagnostics.
- Formatting, linting, and semantic-refactor auditing.
- Agent and skill authoring.
- Docker-backed execution through the project devcontainer.
- Template adoption and updates.
- The complete IWE project-memory workflow.
The catalog is installed during image construction. The devcontainer lifecycle installs it again after persistent agent volumes mount and refreshes a repository's workspace copy on editor attachment.
Skills use the /agentdev:<name> namespace, including /agentdev:pr-open,
/agentdev:pr-review, and /agentdev:pr-merge. See the
catalog README for the complete skill list,
standalone installation instructions, and contributor guidance.
self-improve 0.1.1 is a hook-driven experiential-learning engine for Claude Code.
It captures turns, applies a deterministic gate to decide when a lesson is worth a
review, runs an isolated reviewer that holds no tools, and proposes edits to
CLAUDE.md, rules, and skills. Every mutation is authorized by the user against
exact bytes, targets a normative path allowlist, and is reversible. Its four skills
are /self-improve:improve, apply, reject, and rollback.
The plugin is published from the Claude marketplace and not enabled by anything
this repository ships: the tracked agent settings name it nowhere, and the Ansible
catalog role stages agentdev alone. Enable it deliberately, per session or per
project.
Its runtime is standard-library-only — a rule enforced by a test that walks every
runtime import — so the plugin adds no dependency to a consuming project. Tests
that drive a real Claude session and spend model usage are skipped at collection
unless SELF_IMPROVE_RUN_LIVE is set; the root Makefile carries those live
targets. Behavior is specified in
the learning-loop spec,
and the comparative analysis behind it is in docs/research/.
IWE stores product context, architecture, specifications, plans, and releases as a Markdown knowledge graph. The catalog provides a complete workflow:
| Skill | Purpose |
|---|---|
iwe-setup |
Onboard an existing project and establish its product context. |
iwe-explore |
Investigate ideas or GitHub issues without changing code. |
iwe-plan |
Plan work with verified anchors and risk-scaled specification impact. |
iwe-capture |
File complete, deduplicated bugs, proposed features, and tasks. |
iwe-implement |
Execute plans task by task and record evidence. |
iwe-branch-name |
Name a plan's next implementation or shipping branch. |
iwe-verify |
Check implementation claims against code and specifications. |
iwe-ship |
Block CRITICAL findings and record released behavior. |
iwe-weekly |
Summarize project status and graph health. |
iwe-audit |
Keep durable documents free of session residue. |
iwe-implement-all/iwe-ship-all |
Process all eligible plans in sequence. |
Plan checkboxes carry traceable evidence, and behavior-changing plans state their intended contract before implementation. Machine-managed dependency pins can be masked from map freshness checks without hiding structural source changes.
Consumers adopting IWE receive a schema-valid seed without inheriting this publisher repository's project knowledge.
The optional ai-responder.yml workflow performs Claude-powered reviews and handles
authorized @claude tasks. Its merge gate stays pending while review runs and
requires an accepted AI review.
Security gates prevent the responder from checking out or executing untrusted fork code and restrict task execution to actors with write access. Reviews use the pull request branch's own catalog, so changes to agent workflows are reviewed as changed. To request another review after new work lands, remove the pull request's author as assignee and add them back.
Adopters must configure the CLAUDE_CODE_OAUTH_TOKEN repository secret and the
claude-review environment before enabling the workflow.
Pull-request descriptions separate completed Verification evidence from open
Reviewer Handoff tasks. Repository-specific additions belong in
.github/pr-description-guidance.md.
validate_agent_files 1.0.0 validates agents, skills, prompts, plugin manifests,
marketplace metadata, references, and plugin layout. Repository discovery respects
.gitignore, while explicitly named files remain directly validatable.
--recommend enables warning-level guidance and --errors-only suppresses it.
Pre-commit is the single lint and formatting gate: CI runs the same hooks in the pinned devcontainer image, alongside the validator suites, knowledge-base gates, and image builds.
The firewall is installed but disabled by default. Enable it and edit the allowlist:
// .devcontainer/devcontainer.json
"containerEnv": { "ENABLE_FIREWALL": "true" }Rules come from .devcontainer/firewall-allowlist.txt.
When enabled, the firewall default-denies IPv4 egress, blocks IPv6, preserves Docker
DNS, and verifies both an allowed and a blocked destination during startup.
The supplied devcontainer runs in privileged mode for nested Docker and optional GPU access. Use it only with trusted repositories and container contents.
Xpra starts on display :100 and container port 14500. Open the Xpra HTML5
entry in VS Code's Ports panel to reach the desktop; concurrent worktrees receive
distinct local ports. Prefix graphical applications with vglrun for GPU
acceleration. Advanced users can manage the service with
agentdev-runtime xpra --background, --stop, or --port <n>.
Build the images from the repository root:
docker build -t local/ubuntu-ansible docker/ansible
docker buildx build \
-f docker/base/agentdev-base.Dockerfile \
--build-arg FROM_IMAGE=local/ubuntu-ansible \
-t local/agentdev-base .
docker buildx build \
-f docker/devcontainer/agentdev-devcontainer.Dockerfile \
--build-arg FROM_IMAGE=local/agentdev-base \
-t local/agentdev-devcontainer .docker/deploy/agentdev-deploy.Dockerfile builds agentdev-deploy the same way.
Install dependencies and run the primary repository checks:
uv sync --all-groups
uv run validate_agent_files --recommend . --require-marketplace claude
uv run pytest
uv run ansible-lint ansible
uv run ansible-playbook --syntax-check ansible/playbooks/setup-dev.ymlThe independently released validator must also pass outside the publisher project:
cd py_packages/validate_agent_files
uv run --isolated --extra dev pytestThe canonical plugin sources are
.agents/plugins/agentdev/ and
.agents/plugins/self-improve/. Repository
conventions live in AGENTS.md, while the
template boundary classifies
the reusable, customizable, and publisher-only parts of the tree.
MIT — see LICENSE.