Skip to main content
Glama
README.md
# Windlass

A looped agent orchestrator. Windlass runs Claude Code sessions against machine-checkable success criteria and retries until the criteria pass or a stop condition fires.

The core idea: an agent loop is gather, act, verify. The exit condition is named up front, before the agent runs, and it must be machine-checkable. A task succeeds when its HTTP check returns 200, its build command exits 0, or its test suite passes. It never succeeds because the agent says it's done. When a check fails, Windlass feeds the specific failure back into the next attempt's prompt, so each retry starts from evidence instead of a blank slate. Every attempt has a retry cap, a wall-clock timeout, and an audit trail on disk.

## What it is

Two entry points over one engine:

- **MCP server** (`dist/index.js`): exposes the orchestrator as tools any MCP client can call.
- **CLI** (`windlass`): run task files, check status, browse history, scaffold new definitions.

The engine spawns `claude --print` as a subprocess for each attempt, captures its output, and evaluates the declared criteria against the output and the real world (HTTP endpoints, files, build and test commands).

## MCP tools

The server registers three tools (see `src/mcp/server.ts`):

| Tool | What it does |
|---|---|
| `run_task` | Run a task with success criteria and retry loops. Accepts an inline task definition or a path to a YAML/JSON file. A file containing a `tasks` array runs as a pipeline. |
| `check_task` | Fetch the status of a task execution by ID. |
| `list_tasks` | List running and recent executions from the audit log. |

## Install

Requires Node.js 20+ and the Claude Code CLI (`claude`) on your PATH.

```bash
git clone https://github.com/blakestone-x/windlass.git
cd windlass
npm ci
npm run build
```

### MCP configuration

Add the server to your MCP client config. For Claude Code, in `.mcp.json`:

```json
{
  "mcpServers": {
    "windlass": {
      "command": "node",
      "args": ["/path/to/windlass/dist/index.js"]
    }
  }
}
```

### CLI quickstart

```bash
# Scaffold a task definition
node dist/cli.js define test -o my-test.yaml

# Validate without executing
node dist/cli.js run my-test.yaml --dry-run

# Run it
node dist/cli.js run my-test.yaml --verbose

# Inspect
node dist/cli.js status
node dist/cli.js history -n 10
```

## Task definitions

A task is a YAML or JSON file: a prompt for Claude Code plus the criteria that decide success. Example (adapted from `src/tasks/build.yaml`):

```yaml
name: build-project
type: build
prompt: |
  Build the project using the standard build command.
  If there are compilation errors, read each error, fix the source, and rebuild.
model: sonnet
max_retries: 3
timeout_minutes: 10
escalation: stop
retry_backoff: exponential
retry_delay_seconds: 5
success_criteria:
  - type: build_succeeds
    command: "npm run build"
```

### Success criteria

Six check types, defined in `src/schema/task.ts` and evaluated in `src/evaluators/`:

| Type | Passes when |
|---|---|
| `http_status` | URL returns the expected status, with optional `body_contains` / `body_not_contains`. |
| `grep_output` | A regex matches (or doesn't, with `should_match: false`) the session's stdout, stderr, or transcript. |
| `file_exists` | A file exists, optionally containing a given string. |
| `test_passes` | A test command succeeds and the parsed pass rate meets `min_pass_rate`. Understands Jest/Vitest, pytest, and cargo output. |
| `build_succeeds` | A build command exits 0. |
| `screenshot` | The URL is reachable. Visual review itself is a placeholder for now; the check logs the description for manual verification. |

Tasks can also declare `pre_checks` (gate before the agent runs) and `post_checks` (extra gate after success criteria pass). A post-check failure fails the task.

## The loop and its stop conditions

For each task (`src/engine/orchestrator.ts`):

1. Run pre-checks. If any fail, the task fails without spawning a session.
2. Spawn a Claude Code session with the prompt.
3. Evaluate every success criterion against the session output and the environment.
4. All pass: run post-checks, then mark succeeded.
5. Any fail: log the failure, wait out the backoff delay, and retry with a rebuilt prompt that includes the original task, the specific failed checks, and the tail of the last output.

Stop conditions are wired in, not implied:

- **`max_retries`**: the loop runs at most `max_retries + 1` attempts, then fails with the configured `escalation` label (`notify`, `revert`, or `stop`).
- **`timeout_minutes`**: a wall-clock budget for the whole task, checked before each attempt and passed down to the session subprocess, which gets SIGTERM then SIGKILL.
- **Retry backoff**: `fixed`, `linear`, or `exponential` on `retry_delay_seconds`.

### Pipelines

A file with a `tasks` array runs as a pipeline. Tasks declare `depends_on`; a topological scheduler (`src/engine/scheduler.ts`) rejects cycles, runs ready tasks in parallel up to `max_concurrent`, skips tasks whose dependencies failed, and stops early when `fail_fast` is set.

### Audit log

Every execution, session, and failure analysis is written to SQLite at `.windlass/audit.db` (override the directory with `WINDLASS_DATA_DIR`). The `status` and `history` CLI commands and the `check_task` / `list_tasks` MCP tools read from it, so a crashed process loses no history.

## Status

Extracted from a private orchestration stack built to automate engineering work at a national commercial field-service operation. This is its first public release. The core loop, evaluators, pipelines, and audit log are working; the screenshot criterion is a reachability check awaiting a real visual-review backend. Expect the API to move.

## License

MIT. See [LICENSE](LICENSE).