diff --git a/Dockerfile b/Dockerfile index b792468..1964913 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,4 +37,10 @@ RUN curl -fsSL https://claude.ai/install.sh | bash && \ rm -rf ~/.local/share/claude ~/.local/bin/claude ~/.claude ~/.claude.json \ ~/.cache/claude +# Install Pi Coding Agent globally with npm so it lands in the system +# prefix and survives the home directory bind mount (same approach as +# pi's own docs/containerization.md) +RUN sudo npm install -g --ignore-scripts @earendil-works/pi-coding-agent && \ + sudo npm cache clean --force + WORKDIR /home/${USERNAME} diff --git a/README.md b/README.md index 613f913..a9fd24a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # agent-container -Run OpenCode and Claude Code inside a shared Arch Linux Docker container that closely mirrors a local development environment, while limiting access to sensitive files on the host. +Run OpenCode, Claude Code, and Pi (pi.dev) inside a shared Arch Linux Docker container that closely mirrors a local development environment, while limiting access to sensitive files on the host. ## Features -- Arch Linux-based image with both OpenCode and Claude Code pre-installed +- Arch Linux-based image with OpenCode, Claude Code, and Pi pre-installed - Runs as the host user (same username, UID, GID) - **Per-project isolation**: Each project gets its own container (identified by project path hash) - **Shared persistent home**: All containers mount the same home directory from XDG_DATA_HOME, allowing tools and credentials to persist across projects @@ -29,7 +29,7 @@ Source the helper file `agent.aliases` in your shell configuration (`.bashrc` or source ~/Projects/agent-container/agent.aliases ``` -This makes the `opencode`, `claude`, and `agent-container` commands available in new sessions. +This makes the `opencode`, `claude`, `pi`, and `agent-container` commands available in new sessions. The container home directory at `$XDG_DATA_HOME/agent-container/container-home/` serves as a central `$HOME` inside every container, independent of which project directory you start in. Everything written to `$HOME` inside the container persists there. @@ -39,6 +39,7 @@ Host environment variables matching the following prefixes are automatically for - `OPENCODE_*` -- passed through to OpenCode - `ANTHROPIC_*`, `CLAUDE_*` -- passed through to Claude Code +- `PI_*` plus provider key prefixes (`ANTHROPIC_*`, `OPENAI_*`, `GEMINI_*`, `GROQ_*`, `OLLAMA_*`, and others) -- passed through to Pi. Alternatively log in once with `/login` inside the container; credentials persist in the shared container home. This means inline overrides work as expected: `OPENCODE_CONFIG=foo opencode` @@ -56,13 +57,16 @@ opencode # Run Claude Code claude + +# Run Pi +pi ``` The image is built automatically on first use if it does not already exist. The tool starts inside the container with the current directory mounted and set as the working directory. ### Updating -To rebuild the image with the latest versions of both OpenCode and Claude Code: +To rebuild the image with the latest versions of OpenCode, Claude Code, and Pi: ```sh agent-container update diff --git a/agent-container.py b/agent-container.py index a104018..82dd9e9 100755 --- a/agent-container.py +++ b/agent-container.py @@ -18,6 +18,7 @@ USAGE = """\ Usage: agent-container opencode [args...] Run OpenCode in the container agent-container claude [args...] Run Claude Code in the container + agent-container pi [args...] Run Pi in the container agent-container update Rebuild image with latest versions agent-container purge Remove all containers, image, and data """ @@ -80,6 +81,41 @@ class AgentContainer: env_prefixes=["ANTHROPIC_", "CLAUDE_"], extra_env={"DISABLE_AUTOUPDATER": "1"}, ) + elif command == "pi": + # Forward pi's own variables plus the API key prefixes of its + # supported providers (see pi docs: providers.md). Auth via + # /login persists in the shared container home instead. + self._run_tool( + "pi", + args, + env_prefixes=[ + "PI_", + "ANTHROPIC_", + "ANT_LING_", + "OPENAI_", + "AZURE_", + "GOOGLE_", + "GEMINI_", + "DEEPSEEK_", + "NVIDIA_", + "MISTRAL_", + "GROQ_", + "CEREBRAS_", + "CLOUDFLARE_", + "XAI_", + "OPENROUTER_", + "AI_GATEWAY_", + "ZAI_", + "KIMI_", + "MINIMAX_", + "QWEN_", + "OPENCODE_", + "FIREWORKS_", + "TOGETHER_", + "BASETEN_", + "OLLAMA_", + ], + ) elif command == "update": self.update() elif command == "purge": @@ -221,6 +257,10 @@ class AgentContainer: cmd = [ "docker", "build", + # Use host networking during build so the build works on hosts + # where the default bridge network cannot create veth pairs + "--network", + "host", "--build-arg", f"USERNAME={self.host_username}", "--build-arg", diff --git a/agent.aliases b/agent.aliases index 4232b57..87697f0 100644 --- a/agent.aliases +++ b/agent.aliases @@ -12,6 +12,10 @@ claude() { python3 "$AGENT_CONTAINER_DIR/agent-container.py" claude "$@" } +pi() { + python3 "$AGENT_CONTAINER_DIR/agent-container.py" pi "$@" +} + agent-container() { python3 "$AGENT_CONTAINER_DIR/agent-container.py" "$@" }