Skip to content

stass/autoeng

Repository files navigation

autoeng

Autonomous, plan-driven coding-agent orchestrator (Ruby).

autoeng takes a markdown plan and drives it to a PR-ready branch: it implements each task with a coder agent, reviews every commit with a (possibly different) reviewer agent, runs your test suite as a verification gate, replans when reality diverges from the plan, and stops cleanly when it is done or a budget trips. It is built to be left unattended — every external command runs under a hardened ProcessRunner with hard/idle timeouts and process-group kills, so it does not hang.

The full architecture and rationale live in DESIGN.md, which is the source of truth. This README is the user-facing overview.

Requirements

  • Ruby >= 3.2
  • A POSIX platform (macOS, Linux, or FreeBSD; Windows is out of scope)
  • At least one agent CLI on your PATH:

By default the coder runs on Claude and the reviewer on Codex (cross-engine review is a stronger signal than self-review). If only one CLI is installed, autoeng falls back to same-engine review and prints a warning.

Install

From a checkout:

git clone https://github.com/stass/autoeng
cd autoeng
bundle install
bundle exec bin/autoeng --help

Or build and install the gem:

gem build autoeng.gemspec
gem install ./autoeng-*.gem
autoeng --help

Quick start

Write a plan (or generate one — see Interactive plan creation). A plan is plain markdown; tasks are ### Task <id>: headers with a free-form prose body describing the work:

# Add a rate limiter

## Context
The public API needs per-client request throttling.

## Tasks

### Task t1: Add a token-bucket limiter
Implement `RateLimiter` with a configurable bucket size and refill rate;
unit-test refill, burst, and exhaustion.

### Task t2: Wire the limiter into the Rack middleware
Apply per-client limits keyed on the API token, returning 429 with a
`Retry-After` header when exhausted.

Then run it from inside your project's git repository:

autoeng docs/plans/add-rate-limiter.md

autoeng creates a branch (derived from the plan filename), works task by task, and leaves a reviewed, verified branch ready for a pull request. State and logs land under ./.autoeng/; interrupt with Ctrl-C at any time and --resume later.

How it works

A run is a deterministic, single-threaded state machine over phases:

IMPLEMENT -> COMMIT_REVIEW -> VERIFY -> REPLAN? -> BRANCH_REVIEW -> FINALIZE -> DONE
  • Implement — the coder is given the next pending task in a fresh session and commits its work. The claimed completion is cross-checked against plan and git world-state (an agent saying "done" never overrides what actually happened).
  • Commit review — each new commit is reviewed in order against its scoped diff. Findings are handed back to the coder as an immediate scoped fix commit, reviewed once more, bounded by review.max_rounds. Framework/bookkeeping commits (carrying the configured trailer) are excluded.
  • Verify — a verification command (auto-detected or configured) must pass for a task to count as done; on failure the output is fed back to the coder, or it triggers a replan/block per verify.on_failure.
  • Replan — when an agent emits a needs_replan signal, the plan is mutated through pure, provenance-recording operations (insert/split/append/block/ revise/mark-done — never delete), within replan.max_replans, and committed as a bookkeeping commit.
  • Branch review — a whole-branch backstop review (base...HEAD) across the active lenses.
  • Finalize — off by default; no history rewriting unless enabled.

Agents communicate decisions back to the supervisor through a structured signal protocol (task_complete, task_failed, needs_replan, review_clean, review_findings, question, plan_draft, plan_ready) embedded in their output; everything else is treated as narration.

CLI

autoeng [PLAN_FILE] [options]      # full run (picker if PLAN_FILE omitted)

Run autoeng --help for the complete flag list. Durations accept 30m, 90s, 4h forms throughout.

Modes

Flag Effect
(none) Full run: implement, review, verify, replan, branch review.
--plan "DESC" Interactive plan creation from a description.
--review Review existing branch changes only.
--tasks-only Implement (with per-commit review); skip the branch-review backstop.
--resume Continue the latest run under ./.autoeng/.

Modes are filters over the same phase list, not separate code paths.

Engines & roles

Flag Effect
--coder ENGINE Engine for the coder role (claude | codex).
--reviewer ENGINE Engine for the reviewer role.
--planner ENGINE Engine for the planner role.
--model ROLE=MODEL[:EFFORT] Per-role model override; repeatable (e.g. --model coder=opus --model reviewer=gpt-5.5:medium).

Behavior

Flag Effect
--no-replan Disable dynamic replanning.
--no-per-commit-review Review only at the branch level.
--max-iterations N Global iteration ceiling.
--worktree Run inside a dedicated git worktree.
--branch NAME Branch name (default: derived from the plan filename).
--base-ref REF Base ref to branch from / diff against (default: auto-detected default branch).
--verify CMD Verification command; "" disables the gate, auto detects it.

Finalize (history rewriting) is off by default and configured via the finalize.* config keys, not a flag.

Budgets & reliability

Flag Effect
--max-cost-usd N Dollar spend ceiling (off by default).
--max-commits N Halt if the branch grows beyond N commits.
--max-wall-clock DUR Halt the run after this long.
--wait DUR Wait out rate limits up to DUR, then retry.

Per-call reliability.hard_timeout / idle_timeout / git_timeout and the anti-spin budgets.max_no_progress guard are always on and configured in config.yml (see below).

Output

Flag Effect
--verbose Stream full tool inputs/outputs and reasoning live.
--debug Add raw protocol and ProcessRunner diagnostics.
--no-color Disable ANSI color.
--quiet Suppress terminal narration.
--config-dir DIR Config directory to use ($AUTOENG_CONFIG_DIR).

config subcommands

Command Effect
config init [--repo] Write a commented example config (home dir, or ./.autoeng/ with --repo).
config show Print the effective merged config with each key's source layer annotated.
config dump-defaults DIR Extract the embedded config.yml, prompts, and review lenses into DIR for customization.

Configuration

autoeng works out of the box with zero config — the defaults ship embedded in the gem. To customize, layer your own files on top. Precedence (lowest to highest):

embedded share/config.yml  ->  ~/.config/autoeng/  ->  ./.autoeng/  ->  CLI flags

Merging is a plain deep merge, and because YAML distinguishes "key absent" from "key present but falsy", overriding a default with false/0/[] works without sentinels. autoeng config show tells you which layer each effective value came from.

Highlights of the schema (see share/config.yml or config dump-defaults for the fully-commented version):

  • engines — how to invoke each agent CLI (command, args, model, effort, sandbox). Only argv assembly and event normalization are engine-specific.
  • roles — the role→engine matrix (planner/coder/reviewer/ finalizer); defaults to coder=claude, reviewer=codex.
  • role_models — optional per-role model/effort overrides.
  • reviewper_commit, lenses, max_rounds, stalemate_patience, dedup_findings.
  • verifycommand (auto/""/explicit), on_failure (feedback/replan/block), max_attempts.
  • replanenabled, max_replans.
  • reliabilityhard_timeout, idle_timeout, git_timeout, script_timeout, heartbeat, retry_on_timeout, wait.
  • budgetsmax_no_progress, max_wall_clock, max_commits, max_cost_usd (0/null disables a guard).
  • logginglevel (info/verbose/debug), always-on transcript, dir, color (terminal color; --no-color overrides).
  • gitcommand, default_branch (auto-detect when null), worktree, commit_trailer for bookkeeping commits.
  • finalizeenabled (off), rebase, squash.
  • planmax_rounds (max planner conversation turns before giving up).
  • notify — a POSIX completion/failure hook (off by default).
  • plans_dir, max_iterations.

Prompts & review lenses

Prompts (share/prompts/*.erb) and review lenses (share/agents/*.md) resolve per-file with the same precedence: a matching file in the repo wins, then the home config dir, then the embedded default. You never need any of these in your repo — run config dump-defaults DIR to get editable copies of the ones you want to override.

Logs, artifacts & resume

Everything a run produces lands under ./.autoeng/ (git-ignored automatically):

  • logs/<run-id>/*.jsonl — the lossless transcript: every normalized event, byte-for-byte, one JSON object per line.
  • logs/<run-id>/progress.txt — the human-readable narrative (ANSI-free; color is terminal-only).
  • logs/<run-id>/run.json — the machine-readable run report on completion: tasks completed, commits, findings by severity, replans and reasons, verification results, tokens/cost, and duration.
  • logs/latest — symlink to the most recent run's logs.
  • runs/<run-id>/state.json — persisted RunState, written after every transition, plus a flock lock file marking a live run.

Because the plan file and git history are the real source of truth, --resume reconstructs exactly where an interrupted run stopped. A Ctrl-C triggers a graceful shutdown — the engine process group is killed and logs flushed within a bounded window — then halts the run resumably: a halted state is written and the worktree kept, so --resume picks up from the last commit. (To discard a run instead, use the pause hotkey's Abort.)

Interactive plan creation

autoeng --plan "Add per-client rate limiting to the public API"

The planner explores the repo and proposes a plan, asking clarifying questions (answered via a picker). You can accept, revise (free-text feedback, or by editing the draft in-terminal and sending the edit back as your revision), or reject. On accept the plan is written to plans_dir and you can continue straight into a full run. With --quiet (unattended), it proceeds on logged assumptions instead of pausing for input.

Reliability invariants

These are non-negotiable and enforced repo-wide:

  • Every external command (engines, git, scripts) goes through ProcessRunner — no backticks, system, Open3.capture*, or bare Process.spawn elsewhere.
  • No Timeout.timeout anywhere; deadlines come only from the ProcessRunner watchdog plus process-group signals, on a monotonic clock.
  • POSIX-only, no unbounded blocking reads.

Development

bundle exec rspec      # full test suite (FakeEngine; never spends real credits)
bundle exec rubocop    # lint

Pure cores (signal/plan parsing, config merge, replan ops, finding dedup) are unit-tested directly; the supervisor, phases, and engine adapters are driven by a scripted FakeEngine so no test ever invokes real claude/codex.

License

BSD.

About

Autonomous, plan-driven coding-agent orchestrator

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages