vet

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

commit 8668c1398ce1616e9a40d9c78ad1deca05603683
parent ebc9719ab73fe1aefe26d537a3222f11ff8014b1
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date:   Thu, 26 Feb 2026 13:16:17 -0600

Improved devex, run vet from containers, more appropriate messaging around claude code usage (#163)

* Remove dev/Dockerfile in favor of Containerfile

* Add vet.sh wrapper for running vet in a container with full arg passthrough

Refactors dev scripts to share runtime detection via common.sh.
Both vet.sh and run.sh now auto-build the image before running,
with build output suppressed unless there's an error.

* Refactor dev scripts: split vet.sh into vet.sh + vet-claude.sh with shared common.sh

Extract run_vet helper into common.sh and replace the arg-scanning
auto-detection in vet.sh with two thin wrappers: vet.sh (base image)
and vet-claude.sh (Claude Code image). Update DEVELOPMENT.md to
document the two-script approach.

* Simplify DEVELOPMENT.md containerized section

Merge the Agentic Verifier and Building Images Manually subsections
into the Running Vet and Interactive Development sections, eliminating
redundant examples and reducing the container docs by ~25 lines.

* Replace vet-claude.sh with I_CHOOSE_CONVENIENCE_OVER_FREEDOM env var

A single env var in .env now controls whether Claude Code is included
in the container image. All scripts (vet.sh, run.sh, build.sh) read
it via common.sh — no separate scripts or arguments needed. Delete
vet-claude.sh and simplify DEVELOPMENT.md accordingly.

* Simplify common.sh to globals only, remove all functions

common.sh now only provides shared config (runtime detection, .env
sourcing, IMAGE_NAME/INSTALL_CLAUDE globals). No functions — vet.sh
and run.sh inline the silent-build pattern and container run commands
directly. Each script reads top-to-bottom as a complete unit.

* Agent harness messaging

* Updated wordin

* Honest messaging

* Fix env issue

* Formatting?

---------

Co-authored-by: andrewlaack <andrew@laack.co>
Diffstat:
MDEVELOPMENT.md | 45++++++++++++++-------------------------------
Mdev/build.sh | 27+++++----------------------
Adev/common.sh | 40++++++++++++++++++++++++++++++++++++++++
Mdev/run.sh | 21++++++---------------
Adev/vet.sh | 14++++++++++++++
Mvet/imbue_core/agents/agent_api/claude/client.py | 4+++-
Mvet/imbue_core/agents/agent_api/codex/client.py | 4+++-
7 files changed, 85 insertions(+), 70 deletions(-)

diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md @@ -20,51 +20,34 @@ You can use the `Containerfile` in `dev/` at the repo root to create a container #### Setup -##### Basic Setup +Create a `.env` file at the repo root with your API keys. The recommended keys are `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `CODEX_API_KEY`. -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`. +To include Claude Code in the image add: -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 -./dev/run.sh +I_CHOOSE_CONVENIENCE_OVER_FREEDOM=true ``` -You can then run Vet with: +Without this Claude Code will not be installed in the image. -```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 +#### Running Vet in a Container ```bash -./dev/build.sh claude +./dev/vet.sh --list-models +./dev/vet.sh "check for bugs" --base-commit main +./dev/vet.sh --base-commit main --agentic --agent-harness codex +./dev/vet.sh --base-commit main --agentic --agent-harness claude # requires I_CHOOSE_CONVENIENCE_OVER_FREEDOM=true ``` -Then, to start a container based on this image run: +The image is built automatically on each run. This process should be fast due to layer caching. + +#### Interactive Development ```bash -./dev/run.sh claude +./dev/run.sh ``` -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. +Starts an interactive shell in the container. The repo is bind-mounted at `/app`. ## Formatting Hooks diff --git a/dev/build.sh b/dev/build.sh @@ -1,25 +1,8 @@ #!/bin/bash - -INSTALL_CLAUDE=false -IMAGE_NAME="vet" - -if [ "$1" = "claude" ]; then - INSTALL_CLAUDE=true - IMAGE_NAME="vet-claude" -fi - -if command -v podman &> /dev/null; then - RUNTIME="podman" -elif command -v docker &> /dev/null; then - RUNTIME="docker" -else - echo "No containerization program detected. Please install podman (preferred) or docker." - exit 1 -fi +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh" $RUNTIME build \ - --build-arg INSTALL_CLAUDE=$INSTALL_CLAUDE \ - -t $IMAGE_NAME \ - dev/. - -echo "Built '$IMAGE_NAME' image using $RUNTIME." + --build-arg INSTALL_CLAUDE="$INSTALL_CLAUDE" \ + -f "$SCRIPT_DIR/Containerfile" \ + -t "$IMAGE_NAME" \ + "$SCRIPT_DIR/." diff --git a/dev/common.sh b/dev/common.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +if command -v podman &> /dev/null; then + RUNTIME="podman" +elif command -v docker &> /dev/null; then + RUNTIME="docker" +else + echo "No containerization program detected. Please install podman (preferred) or docker." + exit 1 +fi + +[ -f "$REPO_ROOT/.env" ] || { echo '.env file not found, please create one before proceeding'; exit 1; } +set -a; source "$REPO_ROOT/.env"; set +a + +IMAGE_NAME="vet" +INSTALL_CLAUDE="false" +if [ "${I_CHOOSE_CONVENIENCE_OVER_FREEDOM:-}" = "true" ]; then + IMAGE_NAME="vet-nonfree" + INSTALL_CLAUDE="true" + + RED='\033[1;31m' + NC='\033[0m' + echo -e "${RED}" >&2 + echo "WARNING: You are building an image that includes proprietary software." >&2 + echo "" >&2 + echo "By proceeding, you have chosen to surrender your freedom to a" >&2 + echo "corporation that does not respect your rights as a user. The" >&2 + echo "proprietary components included in this image may restrict your" >&2 + echo "ability to study, modify, and share the software you run." >&2 + echo "" >&2 + echo "This is not merely a technical decision, it is an ethical one." >&2 + echo "Every time you run proprietary software, you give up control over" >&2 + echo "your own computing. You deserve better." >&2 + echo "" >&2 + echo "Proceeding anyway... We shall assume you are testing, not using the software." >&2 + echo -e "${NC}" >&2 +fi diff --git a/dev/run.sh b/dev/run.sh @@ -1,23 +1,14 @@ #!/bin/bash +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh" -IMAGE_NAME="vet" - -if command -v podman &> /dev/null; then - RUNTIME="podman" -elif command -v docker &> /dev/null; then - RUNTIME="docker" -else - echo "No containerization program detected. Please install podman (preferred) or docker." +echo "Ensuring $IMAGE_NAME image is up to date..." +if ! build_output=$("$SCRIPT_DIR/build.sh" 2>&1); then + echo "Image build failed:" + echo "$build_output" exit 1 fi -if [ "$1" = "claude" ]; then - IMAGE_NAME="vet-claude" -fi - -[ -f .env ] || { echo '.env file not found, please create one before proceeding'; exit 1; } - $RUNTIME run --rm -it \ --mount type=bind,source="$(pwd)",target=/app \ - --env-file .env \ + --env-file "$REPO_ROOT/.env" \ "$IMAGE_NAME" bash diff --git a/dev/vet.sh b/dev/vet.sh @@ -0,0 +1,14 @@ +#!/bin/bash +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh" + +echo "Ensuring $IMAGE_NAME image is up to date..." +if ! build_output=$("$SCRIPT_DIR/build.sh" 2>&1); then + echo "Image build failed:" + echo "$build_output" + exit 1 +fi + +$RUNTIME run --rm \ + --mount type=bind,source="$(pwd)",target=/app \ + --env-file "$REPO_ROOT/.env" \ + "$IMAGE_NAME" /root/.local/bin/uv run vet "$@" diff --git a/vet/imbue_core/agents/agent_api/claude/client.py b/vet/imbue_core/agents/agent_api/claude/client.py @@ -116,7 +116,9 @@ class ClaudeCodeClient(RealAgentClient[ClaudeCodeOptions]): if not node_installed: raise AgentCLINotFoundError("Claude Code CLI not found. Node.js is required but not installed.") - raise AgentCLINotFoundError("Claude Code CLI not found. Ensure it is installed and available on your PATH.") + raise AgentCLINotFoundError( + "Claude Code CLI not found. Ensure it is installed and available on your PATH, or specify a different harness with --agent-harness." + ) @classmethod def _build_cli_cmd(cls, options: ClaudeCodeOptions) -> list[str]: diff --git a/vet/imbue_core/agents/agent_api/codex/client.py b/vet/imbue_core/agents/agent_api/codex/client.py @@ -98,7 +98,9 @@ class CodexClient(RealAgentClient[CodexOptions]): if not node_installed or not npm_installed: raise AgentCLINotFoundError("Codex CLI not found. Node.js and npm are required but may not be installed.") - raise AgentCLINotFoundError("Codex CLI not found. Ensure it is installed and available on your PATH.") + raise AgentCLINotFoundError( + "Codex CLI not found. Ensure it is installed and available on your PATH, or specify a different harness with --agent-harness." + ) @classmethod def _build_cli_cmd(cls, options: CodexOptions) -> list[str]: