vet

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 7674b8bd35884d0d90c2259e430e57088683fe20
parent 29410300a0ebed52e78c3eb10da0be73af0eb9cd
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date:   Thu, 19 Feb 2026 22:09:27 +0000

Add instructions for setting up a containerized dev environment (#108)

* Updated instructions for running in container.

* Updated installation / development setup

* Messaging fixes and fix issue disallowing running as root in containers.

* Added comments to dockerfile

* Fixed messaging

* Updated docker file note

---------

Co-authored-by: Andrew Laack <andrew@laack.co>
Diffstat:
MDEVELOPMENT.md | 60+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Adev/Dockerfile | 18++++++++++++++++++
Adev/build.sh | 14++++++++++++++
Adev/run.sh | 14++++++++++++++
Muv.lock | 2+-
Mvet/imbue_core/agents/agent_api/claude/data_types.py | 2+-
Mvet/imbue_core/agents/agent_api/data_types.py | 2+-
Mvet/imbue_core/agents/agent_api/transport.py | 12++++++------
Mvet/issue_identifiers/common.py | 2+-
9 files changed, 115 insertions(+), 11 deletions(-)

diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md @@ -4,10 +4,68 @@ For general usage, installation, and configuration, see the [README](README.md). ## Dev Setup +### On your host machine + +Ensure you have [uv](https://docs.astral.sh/uv/getting-started/installation/) installed, and that you have the correct env variables set to run Vet (Vet defaults to Anthropic models so this means you should have your ANTHROPIC_API_KEY set). + +Then run: + +```bash +uv run vet +``` + +### Containerized + +You can use the `Dockerfile` in `dev/` at the repo root to create a container that suffices to run Vet for development purposes. + +#### Setup + +##### Basic Setup + +Create a `.env` file at the repo that contains your API keys you'd like to use with Vet. The recommended API keys are `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `CODEX_API_KEY`. + +NOTE: Claude Code is **not** installed into the image by default. See the agentic verifier section for an explanation. + +Run the following command to build the image: + +```bash +./dev/build.sh +``` + +Run the following command to start a container based on the image: + ```bash -uv sync +./dev/run.sh ``` +You can then run Vet with: + +```bash +uv run vet +``` + +This will be slower the first time you run it because `uv` has to set up your virtual environment, but since the Vet repo is bind mounted into the container, subsequent runs should be fast. + +##### Agentic Verifier + +The agentic verifier calls out to Claude Code or Codex. Codex is part of the image by default, and if you have your `CODEX_API_KEY` set in your `.env` it will be used. As such, no further actions are required to run the agentic verifier with Codex unless you would like to use another auth approach which requires signing into Codex interactively (oauth and such). + +Since Claude Code is proprietary, it is not installed by default. If you wish to have it installed as part of your image, run + +```bash +./dev/build.sh claude +``` + +Then, to start a container based on this image run: + +```bash +./dev/run.sh claude +``` + +NOTE: Without passing `claude` into `build.sh` it will default to the image without Claude Code installed. + +Within the container, you can run `claude` to begin interactive authentication to get it setup. + ## Running Tests ### Unit tests diff --git a/dev/Dockerfile b/dev/Dockerfile @@ -0,0 +1,18 @@ +FROM debian:bookworm-slim + +ARG INSTALL_CLAUDE=false + +# OpenCode is included for in-container development. +# It is not currently supported when doing agentic verification. + +RUN apt-get update \ + && apt-get install -y npm git curl \ + && npm install -g opencode-ai @openai/codex \ + && curl -LsSf https://astral.sh/uv/install.sh | sh + +WORKDIR /app +RUN git config --global --add safe.directory /app + +RUN if [ "$INSTALL_CLAUDE" = "true" ]; then \ + npm install -g @anthropic-ai/claude-code; \ +fi diff --git a/dev/build.sh b/dev/build.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +INSTALL_CLAUDE=false +IMAGE_NAME="vet" + +if [ "$1" = "claude" ]; then + INSTALL_CLAUDE=true + IMAGE_NAME="vet-claude" +fi + +sudo docker build \ + --build-arg INSTALL_CLAUDE=$INSTALL_CLAUDE \ + -t $IMAGE_NAME \ + dev/. diff --git a/dev/run.sh b/dev/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +IMAGE_NAME="vet" + +if [ "$1" = "claude" ]; then + IMAGE_NAME="vet-claude" +fi + +[ -f .env ] || { echo '.env file not found, please create one before proceeding'; exit 1; } + +sudo docker run -it \ + --mount type=bind,source="$(pwd)",target=/app \ + --env-file .env \ + "$IMAGE_NAME" bash diff --git a/uv.lock b/uv.lock @@ -1511,7 +1511,7 @@ wheels = [ [[package]] name = "verify-everything" -version = "0.1.10" +version = "0.1.11" source = { editable = "." } dependencies = [ { name = "anthropic" }, diff --git a/vet/imbue_core/agents/agent_api/claude/data_types.py b/vet/imbue_core/agents/agent_api/claude/data_types.py @@ -7,7 +7,7 @@ from vet.imbue_core.agents.agent_api.data_types import AgentOptions from vet.imbue_core.agents.agent_api.data_types import AgentToolName from vet.imbue_core.pydantic_serialization import SerializableModel -ClaudePermissionMode = Literal["plan", "default", "acceptEdits", "bypassPermissions"] +ClaudePermissionMode = Literal["plan", "default", "acceptEdits", "bypassPermissions", "dontAsk"] class ClaudeMcpStdioServerConfig(SerializableModel): diff --git a/vet/imbue_core/agents/agent_api/data_types.py b/vet/imbue_core/agents/agent_api/data_types.py @@ -10,7 +10,7 @@ from pydantic import Tag from vet.imbue_core.pydantic_serialization import SerializableModel from vet.imbue_core.pydantic_serialization import build_discriminator -AgentPermissionMode = Literal["default", "acceptEdits", "bypassPermissions"] +AgentPermissionMode = Literal["default", "acceptEdits", "bypassPermissions", "dontAsk"] class AgentToolName(enum.StrEnum): diff --git a/vet/imbue_core/agents/agent_api/transport.py b/vet/imbue_core/agents/agent_api/transport.py @@ -159,15 +159,15 @@ class AgentSubprocessCLITransport(AgentTransport[AgentSubprocessCLITransportOpti except subprocess.SubprocessError: pass + stderr_read_thread.join(timeout=5.0) process.wait() if process.returncode is not None and process.returncode != 0: stderr_output = "\n".join(stderr_lines) - if stderr_output and "error" in stderr_output.lower(): - raise AgentProcessError( - "CLI process failed", - exit_code=process.returncode, - stderr=stderr_output, - ) + raise AgentProcessError( + "CLI process failed", + exit_code=process.returncode, + stderr=stderr_output, + ) def is_connected(self) -> bool: process = self._process diff --git a/vet/issue_identifiers/common.py b/vet/issue_identifiers/common.py @@ -232,7 +232,7 @@ def get_agent_options(cwd: Path | None, model_name: str, agent_harness_type: Age model_name = _DEFAULT_CLAUDE_MODEL return ClaudeCodeOptions( cwd=cwd, - permission_mode="bypassPermissions", + permission_mode="dontAsk", allowed_tools=list(READ_ONLY_TOOLS) + [AgentToolName.BASH], model=model_name, )