Development

You need Docker and Make. make help lists every target. Lint and the CI test suite run inside a pinned uv container, so they need nothing else on the host.

Layout

Path Contents
aicodebox/adapters/ The adapter contract, AgentAdapter, and the JSON helpers.
aicodebox/shared/ The runner (sync, streaming, schema retries), model and effort lists, the harness JSON parser, logging.
aicodebox/modes/ One package per mode: api (with the OpenAI and MCP code), telegram, cron, mcp.
aicodebox/tests/ Unit tests, with a stub adapter in conftest.py. Not shipped in the wheel.
entrypoint.sh Container startup and mode dispatch.
Dockerfile, Dockerfile.full The minimal and full images.
full-go/, full-node/, full-python/ Locked tool sets for the full image.
scripts/ Integration tests that run real containers.
docs/ This site.

Commands

Target Does
make build Build psyb0t/aicodebox:v<version> and :latest. The version comes from pyproject.toml.
make build-full Build the minimal image, then :v<version>-full and :latest-full on top of it.
make build-all Both variants.
make lint flake8 and pyright over aicodebox/, in the uv container.
make test The unit tests on the host with your own uv (same as make test-unit).
make test-ci The unit tests in the uv container. This is what CI runs.
make test-integration Build the image, then run the startup hook and identity tests against it.
make test-entrypoint-identity The identity tests against an existing image.
make test-full-image Build the full image and check every listed tool.
make docs Build this site into .testing/docs-site.
make version V=X.Y.Z Set the package version without committing. With no V, print it.
make clean Remove the built images and Python caches.

Tests

The unit tests run in-process, without Docker or a real agent. They set AICODEBOX_ADAPTER=aicodebox.tests.conftest:_StubAdapter, and the fixtures in conftest.py clear the AICODEBOX_* variables a host may have set. They need no credentials and no network once dependencies are installed.

make test-ci runs them as your own UID in ghcr.io/astral-sh/uv:0.13.0-python3.14-trixie-slim, pinned by digest in the Makefile. The virtual environment lives inside the container under /tmp, so the checkout gets no .venv from it, and the workspace root points at a temporary directory because the API modules read it at import time.

The integration tests in scripts/ need Docker and run throwaway containers without a host Docker socket or credentials. To test a changed entrypoint without rebuilding:

ENTRYPOINT="$PWD/entrypoint.sh" make test-entrypoint-identity

Set IMAGE to test another existing image.

Tests with a real agent and a real Telegram chat live in the child repositories, for example the end-to-end harness in pibox, which drives Telegram with telethon-plus.

Lint

make lint runs flake8 with the settings in .flake8 (120 columns) and pyright, both from the lint dependency group in pyproject.toml. Either one failing fails the target.

Lock files

Target Refreshes
make pkg-lock uv.lock, under the exclude-newer cutoff in pyproject.toml.
make full-go-lock go.sum for the full image’s Go tools.
make full-node-lock full-node/pnpm-lock.yaml.
make full-python-lock full-python/requirements.txt, with hashes, from requirements.in.

The Python dependencies the image installs exclude the dev and lint groups.

CI

pipeline.yml runs on every push:

Job When Does
code every branch push make lint and make test-ci.
call-docker-image-workflow after code, on main Builds the minimal image, then the full image on top of it, for amd64 and arm64. Pushes latest, latest-full and per-commit SHA tags, and scans them. Other branches stop after code.
badges after code on main, after wait on a tag Updates the README badges.
wait v* tag Waits up to 180 minutes for the tagged commit’s main run to succeed.
docker-promote after wait Gives the release tag to the images that run already built, without rebuilding.
release after docker-promote Creates the GitHub Release from the annotated tag’s notes.

Releases

A release bumps version in pyproject.toml (make version V=X.Y.Z), adds a CHANGELOG.md entry, lands on main, and gets an annotated vX.Y.Z tag on that commit. CI does the rest. The package reads its __version__ from the installed metadata, so there is no second version string to update.

Docs

The site is Jekyll with the just-the-docs theme, built by GitHub Pages from docs/ on main. make docs builds it locally with the same image Pages uses.

  • 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 breaks on the site.
  • Keep docs/ flat. A file name is its URL.
  • A change to behavior, an endpoint or a setting updates its page in the same change.