1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Also check `~/.claude/CLAUDE.md` for user-level development standards (TDD workflow, git practices, session state management, etc.) that apply globally across all projects.
## Build & Test Commands
```bash
# Build
go build ./...
# Run all tests
go test ./...
# Run a single package's tests
go test ./internal/executor/...
# Run a single test by name
go test ./internal/api/ -run TestServer_CreateTask_MissingName
# Run with race detector (important for executor/pool tests)
go test -race ./...
# Build the binary
go build -o claudomator ./cmd/claudomator/
```
> **Note:** `go-sqlite3` uses CGo. A C compiler (`gcc`) must be present for builds and tests.
## Running the Server
```bash
# Initialize data directory
./claudomator init
# Start API server (default :8484)
./claudomator serve
# Run a task file directly (bypasses server)
./claudomator run ./test/fixtures/tasks/simple-task.yaml
# List tasks via CLI
./claudomator list
```
Config defaults to `~/.claudomator/config.toml`. Data is stored in `~/.claudomator/` (SQLite DB + execution logs).
## Architecture
**Pipeline:** CLI/API → `executor.Pool` → `executor.ClaudeRunner` → `claude -p` subprocess → 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/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 |
| `internal/config` | TOML config + data dir layout |
| `internal/cli` | Cobra CLI commands (`run`, `serve`, `list`, `status`, `init`) |
### Key Data Flows
**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
**State machine** (`task.ValidTransition`):
`PENDING` → `QUEUED` → `RUNNING` → `COMPLETED | FAILED | TIMED_OUT | CANCELLED | BUDGET_EXCEEDED`
Failed tasks can retry: `FAILED` → `QUEUED`
**WebSocket:** `Hub` fans out task completion events to all connected clients. `Server.StartHub()` must be called after creating the server.
### Task YAML Format
```yaml
name: "My Task"
claude:
model: "sonnet"
instructions: |
Do something useful.
working_dir: "/path/to/project"
max_budget_usd: 1.00
permission_mode: "default"
allowed_tools: ["Bash", "Read"]
timeout: "15m"
priority: "normal" # high | normal | low
tags: ["ci"]
```
Batch files wrap multiple tasks under a `tasks:` key.
### Storage Schema
Two tables: `tasks` (with `config_json`, `retry_json`, `tags_json`, `depends_on_json` as JSON blobs) and `executions` (with paths to log files). Schema is auto-migrated on `storage.Open()`.
## ADRs
See `docs/adr/001-language-and-architecture.md` for the Go + SQLite + WebSocket rationale.
|