Development
Docker, Make and Git are enough on the host. Lint, the CI tests and the docs build run in containers.
git clone https://github.com/psyb0t/docker-claudebox.git
cd docker-claudebox
make help
The AGENTS.md file maps the repository, and the rules in .agents/rules/ hold the conventions for each kind of change.
Building the image
make build # psyb0t/claudebox:latest and :v<version>
make build-full # psyb0t/claudebox:latest-full and :v<version>-full
make build-all # both
Both pull their aicodebox base first. To build on a base you built yourself, name it and skip the pull:
make build \
BASE_IMAGE=aicodebox-base:local \
SKIP_BASE_PULL=1
FULL_BASE_IMAGE does the same for build-full, and CLAUDE_VERSION changes the Claude Code version the image pins. The version tag comes from version in claudebox/pyproject.toml.
Lint
make lint # shellcheck, shfmt, ruff and pyright
make format # rewrite shell and Python sources in place
make lint checks every tracked .sh file with shellcheck at warning level and with shfmt -i 4 -ci, then runs ruff check, ruff format --check and pyright on the claudebox/ package. The Python tools run in a development image built from Dockerfile.dev on the same aicodebox base the published image uses, so the adapter is checked against the real aicodebox package.
Tests
| Command | Needs | Covers |
|---|---|---|
make test-ci | Docker | Everything CI runs. No credentials, no image build. |
make test-unit | uv and a docker-aicodebox checkout next to this one | The adapter unit tests, outside a container |
make test | uv, Docker | test-unit, the native output contract, and the first-start install output |
make test-smoke, make test-persist | Docker | Build the minimal image, then boot it |
./test.sh [test_name ...] | Docker, tests/.env | The end-to-end suites against real Claude Code |
make test-ci runs, each in the development image:
- the adapter unit tests in
claudebox/tests/; tests/programmatic/check-contract.sh, which feeds recorded native output through the wrapper and checks it comes out byte for byte;- the wrapper and launcher tests
test_image_select.sh,test_agent_launcher.sh,test_managed_nested.sh,test_cron_shortcut.shandprogrammatic/check-prog-reuse.sh, which put a fakedockerorclaudeonPATH; - with the host Docker socket,
check-install-output.sh,test_entrypoint_aliases.shandtest_entrypoint_identity.sh, which run the entrypoint in throwaway containers to check the first-start install, the variable aliases, and the Git identity.
The end-to-end suites spend real Claude usage. Put a token in tests/.env, using tests/.env.example as the template, and run ./test.sh --help for the list of suites.
Documentation
This site is built from docs/ by GitHub Pages, with Jekyll and the just-the-docs theme.
make docs # build into .testing/docs-site with GitHub's own build image
Every page needs front matter with a unique title and a description of 50 to 170 characters. Link between pages with the relative .md path. Link a file outside docs/ with its full GitHub URL, because a relative link to it breaks on the site.
CI
.github/workflows/pipeline.yml runs on every push:
- Any branch: the
codejob runsmake lintandmake test-ci. master: whencodepasses, both variants are built forlinux/amd64andlinux/arm64, scanned, and pushed as:latest,:latest-fulland the:sha-<commit>tags, and the badges are refreshed.- A
v*tag: no rebuild. The pipeline waits for that commit’smasterrun to succeed, promotes the same image digests to the release tags, creates the GitHub release, refreshes the badges, publishes the skill and the OpenClaw plugin to ClawHub, and updates the MCP registry entry fromserver.json.
Versions
make version # print the current tag
make version V=X.Y.Z # set the version everywhere it is stored
make version V=X.Y.Z updates claudebox/pyproject.toml, the Codex plugin manifest, and the release ref install.sh downloads the wrapper from. It does not commit or tag.