windlass
by blakestone-x
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).
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues