diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-03-18 23:56:20 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-03-18 23:56:20 +0000 |
| commit | 7df4f06ae0e3ae80bd967bf53cbec36e58b4a3bd (patch) | |
| tree | 740c141c52764604fc8d4c036733e5f47368b26a /CLAUDE.md | |
| parent | a4795d68fc5381f1ff48d043fe7554355e5899fb (diff) | |
feat: containerized execution with agent tooling and deployment fixes
- ContainerRunner replaces ClaudeRunner/GeminiRunner; all agent types run
in Docker containers via claudomator-agent:latest
- Writable agentHome staging dir (/home/agent) satisfies home-dir
requirements for both claude and gemini CLIs without exposing host creds
- Copy .credentials.json and .claude.json into staging dir at run time;
GEMINI_API_KEY passed via env file
- Fix git clone: remove MkdirTemp-created dir before cloning (git rejects
pre-existing dirs even when empty)
- Replace localhost with host.docker.internal in APIURL so container can
reach host API; add --add-host=host.docker.internal:host-gateway
- Run container as --user=$(uid):$(gid) so host-owned workspace files are
readable; chmod workspace 0755 and instructions file 0644 after clone
- Pre-create .gemini/ in staging dir to avoid atomic-rename ENOENT on first
gemini-cli run
- Add ct CLI tool to container image: pre-built Bash wrapper for
Claudomator API (ct task submit/create/run/wait/status/list)
- Document ct tool in CLAUDE.md agent instructions section
- Add drain-failed-tasks script: retries failed tasks on a 5-minute interval
- Update Dockerfile: Node 22 via NodeSource, Go 1.24, gemini-cli,
git safe.directory=*, default ~/.claude.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 52 |
1 files changed, 47 insertions, 5 deletions
@@ -53,14 +53,14 @@ Config defaults to `~/.claudomator/config.toml`. Data is stored in `~/.claudomat ## Architecture -**Pipeline:** CLI/API → `executor.Pool` → `executor.ClaudeRunner` → `claude -p` subprocess → SQLite + log files +**Pipeline:** CLI/API → `executor.Pool` → `executor.ContainerRunner` → Docker container → SQLite + log files ### Packages | Package | Role | |---|---| | `internal/task` | `Task` struct, YAML parsing, state machine, validation | -| `internal/executor` | `Pool` (bounded goroutine pool) + `ClaudeRunner` (subprocess manager) | +| `internal/executor` | `Pool` (bounded goroutine pool) + `ContainerRunner` (Docker-based executor) | | `internal/storage` | SQLite wrapper; stores tasks and execution records | | `internal/api` | HTTP server (REST + WebSocket via `internal/api.Hub`) | | `internal/reporter` | Formats and emits execution results | @@ -72,9 +72,9 @@ Config defaults to `~/.claudomator/config.toml`. Data is stored in `~/.claudomat **Task execution:** 1. Task created via `POST /api/tasks` or YAML file (`task.ParseFile`) 2. `POST /api/tasks/{id}/run` → `executor.Pool.Submit()` → goroutine in pool -3. `ClaudeRunner.Run()` invokes `claude -p <instructions> --output-format stream-json` -4. stdout streamed to `~/.claudomator/executions/<exec-id>/stdout.log`; cost parsed from stream-json -5. Execution result written to SQLite; broadcast via WebSocket to connected clients +3. `ContainerRunner.Run()` clones `repository_url`, runs `docker run claudomator-agent:latest` +4. Agent runs `claude -p` inside the container; stdout streamed to `executions/<exec-id>/stdout.log` +5. On success, runner pushes commits back to the remote; execution result written to SQLite + WebSocket broadcast **State machine** (`task.ValidTransition`): `PENDING` → `QUEUED` → `RUNNING` → `COMPLETED | FAILED | TIMED_OUT | CANCELLED | BUDGET_EXCEEDED` @@ -166,6 +166,48 @@ A task is created for: Tasks are tagged `["ci", "auto"]`, capped at $3 USD, and use tools: Read, Edit, Bash, Glob, Grep. +## Agent Tooling (`ct` CLI) + +Agents running inside containers have access to `ct`, a pre-built CLI for interacting with the Claudomator API. It is installed at `/usr/local/bin/ct` in the container image. **Use `ct` to create and manage subtasks — do not attempt raw `curl` API calls.** + +### Environment (injected automatically) + +| Variable | Purpose | +|---|---| +| `CLAUDOMATOR_API_URL` | Base URL of the Claudomator API (e.g. `http://host.docker.internal:8484`) | +| `CLAUDOMATOR_TASK_ID` | ID of the currently-running task; used as the default `parent_task_id` for new subtasks | + +### Commands + +```bash +# Create a subtask and immediately queue it (returns task ID) +ct task submit --name "Fix tests" --instructions "Run tests and fix any failures." [--model sonnet] [--budget 3.0] + +# Create, queue, and wait for completion (exits 0=COMPLETED, 1=FAILED, 2=BLOCKED) +ct task submit --name "Fix tests" --instructions "..." --wait + +# Read instructions from a file instead of inline +ct task submit --name "Fix tests" --file /workspace/subtask-instructions.txt --wait + +# Lower-level: create only (returns task ID), then run separately +TASK_ID=$(ct task create --name "..." --instructions "...") +ct task run "$TASK_ID" +ct task wait "$TASK_ID" --timeout 600 + +# Check status of any task +ct task status <task-id> + +# List recent tasks +ct task list +``` + +### Notes + +- Default model is `sonnet`; default budget is `$3.00 USD`. Override with `--model` / `--budget`. +- `ct task wait` polls every 5 seconds and exits with the task's terminal state on stdout. +- Subtasks inherit the current task as their parent automatically (via `$CLAUDOMATOR_TASK_ID`). +- Override parent with `--parent <task-id>` if needed. + ## ADRs See `docs/adr/001-language-and-architecture.md` for the Go + SQLite + WebSocket rationale. |
