q is a typed Python CLI (built with Typer) that wraps the config/Makefile targets. It replaces raw make invocations with an ergonomic, documented interface.
Package location: app/cli/
Entry point: q (installed via uv)
cd app/cli
uv sync # installs typer, rich, and the q entry point
uv run q --help # verify installationBuilds the claude-agent:wolfi image (no cache).
q build
q build --image my-image:tag --dockerfile Dockerfile.custom| Option | Default | Description |
|---|---|---|
--image |
claude-agent:wolfi |
Image tag |
--dockerfile |
Dockerfile.wolfi |
Dockerfile to use |
Creates the isolated bridge network (claude-agent-net). Idempotent — skips if already exists.
q network
q network --subnet 10.0.0.0/24 --network-name my-netRuns an interactive coordinator shell. Hands off the TTY completely via os.execvp.
q run
q run --cpus 4 --memory 8G --name my-coordinator
q shell # alias for runRequires CLAUDE_CONTAINER_OAUTH_TOKEN to be set.
Spawns a detached headless agent in an isolated git worktree.
q spawn --branch feat/oauth2 --task "Implement OAuth2 with JWT tokens"
q spawn --branch test/payments --task "Write unit tests for payments module" --cpus 4
q spawn --branch feat/quick-fix --task "Tighten the readme" --model sonnet| Option | Required | Description |
|---|---|---|
--branch |
yes | Git branch for the agent worktree |
--task |
yes | Task description (prompt for Claude) |
--cpus |
no | CPU count override |
--memory |
no | Memory limit override (e.g. 8G) |
--image |
no | Image tag override |
--model |
no | Claude model the agent runs (opus default; e.g. sonnet, haiku) |
Requires CLAUDE_CONTAINER_OAUTH_TOKEN to be set.
Agent model: the agent runs
opusunless--modelsays otherwise. It does not inherit the host's~/.claude/settings.jsonmodelpreference — that file is copied into the container for credentials only, and a headless agent must not depend on a personal interactive setting. Drop tosonnetfor lighter tasks. The Makefile passes the choice through as-e AGENT_MODEL.
Branch naming: avoid
/in branch names — they create nested subdirectories in$AGENTS_HOMEwhich the entrypoint cannot handle. Use flat names likefeat-oauth2instead offeat/oauth2.
Lists active agent containers and worktrees on disk.
q agents listShows snapshot logs for a branch agent.
q agents logs --branch feat-oauth2Follows live streaming logs (hands off TTY via os.execvp).
q agents follow --branch feat-oauth2Shows agent status from the persisted status.json file. Works even after the
container has exited — reads directly from the worktree filesystem.
q agents status --branch feat-oauth2Output includes: phase, branch, task, timestamps, duration, exit code, commit count.
Shows structured lifecycle events ([agent:status] markers) for a branch agent.
q agents summary --branch feat-oauth2Stops a branch agent container.
q agents stop --branch feat-oauth2When the container no longer exists (agent finished, --rm removed it), logs and
follow automatically fall back to the persisted .agent/agent.log in the worktree
directory with a contextual message. This avoids confusing error output.
PI agents are a separate agent class backed by a local mlx_lm.server
(managed via /iac). They do not require CLAUDE_CONTAINER_OAUTH_TOKEN
— authentication is local. See pi-agent.md for the full
architecture.
Builds the claude-pi:ubuntu image (Dockerfile.pi).
q pi build
q pi build --image claude-pi:custom --dockerfile Dockerfile.pi.customSpawns a detached headless PI agent (local LLM backend).
q pi spawn --branch pi/refactor --task "rename ambiguous helpers"
q pi spawn --branch pi/explore --task "explore the auth module" \
--cpus 4 --memory 8G \
--base-url http://192.168.100.1:8080/v1 \
--model-id mlx-community/llama-3.1-8b| Option | Required | Description |
|---|---|---|
--branch |
yes | Git branch for the PI worktree |
--task |
yes | Task description for the PI agent |
--cpus |
no | CPU count |
--memory |
no | Memory limit (e.g. 3G) |
--image |
no | PI image tag override |
--base-url |
no | OpenAI-compatible base URL of the local LLM (default = bridge gateway IP, host.containers.internal is NOT supported by Apple Container CLI) |
--model-id |
no | Model id served by mlx_lm.server (must match /v1/models response) |
The Makefile enforces MAX_PI_AGENTS=1 by default — spawn will refuse
to launch a second PI agent while one is still running. The model + 6 GB
prompt cache leaves little RAM headroom on M-series machines.
q pi list # PI agents only
q pi logs --branch pi/refactor # snapshot logs
q pi follow --branch pi/refactor # live logs (TTY hand-off)
q pi status --branch pi/refactor # status.json (works post-exit)
q pi stop --branch pi/refactor # stop the containerThe list command filters by agent_kind=pi in status.json, so it
returns only PI worktrees — Claude agent worktrees in the same
AGENTS_HOME are excluded.
q clean # Remove container + image
q clean-network # Remove bridge network
q clean-all # Remove image + networkAll commands resolve the config/Makefile path dynamically:
git rev-parse --show-toplevel → GIT_ROOT / "config" → make -C <path> <target> [VARS]
Commands that contact the container daemon (run, spawn) call check_token() which validates CLAUDE_CONTAINER_OAUTH_TOKEN is set before invoking make.
TTY-intensive commands (run, shell, agents follow) use os.execvp to replace the process entirely, so the terminal is fully handed off with no intermediary.
app/cli/
├── pyproject.toml ← uv project, entry point: q
└── src/
└── container_cli/
├── main.py ← registers all commands + agents/pi sub-apps
├── targets.py ← Target enum: the config/Makefile target contract
├── utils.py ← git/worktree paths, run_make, check_token, print_agent_status
└── commands/
├── build.py ← build, clean, clean-network, clean-all
├── network.py ← network
├── run.py ← run, shell
├── agents.py ← spawn, list, logs, follow, stop, status, summary
└── pi_agents.py ← pi build/spawn/list/logs/follow/stop/status
Target names are not free strings: every Makefile target the CLI invokes is a member of the
Targetenum intargets.py, andtests/test_targets.pyverifies each one againstconfig/Makefile. Real-container round-trip tests live intests/e2e/— see e2e-tests.md.
| CLI command | Makefile target | Notes |
|---|---|---|
q build |
build |
optional --image, --dockerfile |
q network |
network |
optional --subnet, --network-name |
q run |
run |
TTY hand-off via os.execvp |
q shell |
shell |
alias for run |
q spawn |
spawn |
requires --branch, --task |
q agents list |
list-agents |
|
q agents logs |
logs-agent |
requires --branch |
q agents follow |
follow-agent |
TTY hand-off |
q agents stop |
stop-agent |
requires --branch |
q clean |
clean |
|
q clean-network |
clean-network |
|
q clean-all |
clean-all |
|
q pi build |
build-pi |
optional --image, --dockerfile |
q pi spawn |
spawn-pi |
requires --branch, --task; no Claude token needed |
q pi list |
list-pi-agents |
filters by agent_kind=pi |
q pi logs |
logs-pi-agent |
requires --branch |
q pi follow |
follow-pi-agent |
TTY hand-off |
q pi stop |
stop-pi-agent |
requires --branch |
q pi status |
(local read) | reads $AGENTS_HOME/<b>/.agent/status.json |