You define a task file and pick an agent directory (only OpenCode and Pi support for now) that includes both agent configuration and installation scripts. Crucible starts a Docker container, installs the agent, injects specified task information and/or volumes, and waits for the agent to complete the task. It copies relevant output from the agent's environment into a directory of your choosing.
- Python 3.12+
- Docker with Compose V2 (
docker composecommand) - Bash shell (for agent init scripts)
- Clone the repository:
git clone https://github.com/g-jensen/crucible.git
cd crucible- Create a virtual environment and install dependencies:
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r requirements.txt- Verify Docker is running:
docker compose versionpip install pyinstallThen, build main.py:
pyinstaller --onefile src/main.py -n crucibleMake sure you're in your venv when you run this command.
Add the executable to your PATH somehow
sudo cp dist/crucible /usr/local/bin/crucibleRun the included example task with the OpenCode agent:
crucible \
--run-dir ./result \
--task example/task.yml \
--agent-dir agents/opencodeThis will:
- Create
result/and start a container - Copy the seed workspace into
result/and mount it onto the container - Run the OpenCode agent in the container with the specified config and the task prompt
- Save all outputs to
result/ - Clean up Docker resources
With --results-dir (creates timestamped subdirectory):
crucible \
--results-dir <results-directory> \
--task <path-to-task.yml> \
--agent-dir <path-to-agent-directory>With --run-dir (uses exact directory):
crucible \
--run-dir <exact-output-directory> \
--task <path-to-task.yml> \
--agent-dir <path-to-agent-directory>- Create a task file (
my-task.yml):
id: code_review
docker_image: python:3.11-slim
seed_path: ./code_to_review
prompt: "Review the Python code in the workspace and suggest improvements"- Prepare seed directory (
code_to_review/):
code_to_review/
main.py
utils.py
README.md
- Run Crucible:
crucible \
--results-dir ./results \
--task my-task.yml \
--agent-dir agents/opencode- Check results:
ls results/code_review_*/workspace/
cat results/code_review_*/workspace/review.txtTasks are defined in YAML files with the following structure:
id: example_task
name: Example Task # optional
description: Basic Python I/O task # optional
docker_image: python:3.11-slim
seed_path: ./seed # optional. Path is relative to this file
prompt: "Write and run a simple Python script that writes 'hello' to result.txt"Agent directories only require an init.sh and a docker/docker-compose.yml:
agents/my-agent/
├── init.sh # Host-side setup script (executable)
├── docker/
│ ├── docker-compose.yml # Container orchestration
│ └── entrypoint.sh # Entry point script for docker-compose.yml. Could be stored anywhere.
└── ... (other agent-specific files)
Runs on the host machine before starting the container.
Environment variables available:
ABSOLUTE_RESULT_DIR: Absolute path to this run's result directory
Responsibilities:
- Prepare agent-specific files in
$ABSOLUTE_RESULT_DIR - Copy configuration files
- Generate environment files
- Must be executable (
chmod +x init.sh) - Must include shebang:
#!/bin/bash
Example:
#!/bin/bash
cp -r ./config $ABSOLUTE_RESULT_DIR/agent_config
echo "API_KEY=test" > $ABSOLUTE_RESULT_DIR/.envEnvironment variables available:
IMAGE_NAME: Base Docker image (from task.yml)WORKING_DIR: Container workspace path (fixed:/workspace)ABSOLUTE_RESULT_DIR: Absolute path to run directoryTASK_PROMPT: Task prompt string (from task.yml)
Expected structure:
services:
agent:
image: ${IMAGE_NAME}
working_dir: ${WORKING_DIR}
environment:
- TASK_PROMPT=${TASK_PROMPT}
volumes:
- ./entrypoint.sh:/entrypoint.sh:ro
- ${ABSOLUTE_RESULT_DIR}/workspace:${WORKING_DIR}:rw
- ${ABSOLUTE_RESULT_DIR}/agent_config:/config:rw
command: ["/entrypoint.sh"]This can be shared between agent directories and runs inside the container as defined in docker-compose.yml.
Responsibilities:
- Install the agent (from internet, package manager, etc.)
- Configure the agent
- Execute the agent with
$TASK_PROMPT - Exit with appropriate code (0 = success)
Example:
#!/bin/bash
set -euo pipefail
# Install agent
apt-get update && apt-get install -y curl
npm install -g my-ai-agent
# Run agent
my-ai-agent run "$TASK_PROMPT"Docker creates files as root inside containers. If you see permission errors when cleaning up, this is normal. Results are still saved correctly.
To clean up manually:
sudo rm -rf results/example_task_*/Check the agent's entrypoint.sh script:
- Verify it has a shebang:
#!/bin/bash - Verify it's executable:
chmod +x agents/*/docker/entrypoint.sh - Check any logs in the run directory
This is expected behavior. Crucible logs task failures (exit code ≠ 0) but continues to preserve results. Check:
docker logsoutput (shown during run)- Files in the run directory's workspace
- Agent-specific log files
Run the unit test suite:
source .venv/bin/activate
pytest src/test/ -vRun the integration test:
./test_integration.sh