* refactor(release): split publish into two-repo model Org compliance forbids npm publish credentials in public repositories. Move the npm publish step out of this repo and into a private ComposioHQ/ao-publisher repo, triggered via repository_dispatch. Public repo (this one) now only: - Bumps versions via changesets/action (no `publish:` argument) - Creates git tags via `pnpm changeset tag` - Creates the GitHub release (stable or prerelease) - Dispatches `publish-npm-stable` / `publish-npm-nightly` to ao-publisher The only secret needed here is PUBLISHER_DISPATCH_TOKEN, a fine-grained PAT scoped to ao-publisher with `repository_dispatch:write`. NPM_TOKEN lives in ao-publisher's secret store and never enters this repo. Removed from both workflows: NODE_AUTH_TOKEN env, NPM_CONFIG_PROVENANCE env, and `registry-url` on setup-node — none are needed when no npm publish happens here. Canary also gains a skip-if-unchanged guard so cron ticks during quiet stretches don't republish identical SHAs. CONTRIBUTING.md "Release Setup" → "Release Architecture": documents the two-repo split, secret layout, rotation procedure, and the stable vs. nightly flows. Requires PUBLISHER_DISPATCH_TOKEN secret + ao-publisher repo setup by maintainers (out of scope for this PR — see PR description). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(release): address Greptile review findings on two-repo split Three review findings on PR #1815: 1. (P1) canary.yml missing git commit before tagging `pnpm changeset version --snapshot` modifies package.json but does not commit. `pnpm changeset tag` would then tag the pre-snapshot HEAD, so ao-publisher would check out un-bumped versions. Add a "Commit snapshot version bumps" step with `git diff --cached --quiet || git commit` (defensive: skip if nothing to commit) and the `[skip ci]` marker. The commit is never pushed to main — only the tags are pushed and the orphan commit travels with them. 2. (P2) release.yml `--target main` race condition If a commit lands on main between `git push --follow-tags` and `gh release create --target main`, the release commitish drifts and auto-generated notes pull in unrelated commits. Create an explicit `vX.Y.Z` git tag pointing at the version-bump commit, push it, and drop `--target`. `gh release create` then resolves the commitish from the existing tag — no race window. 3. (P2) canary.yml skip-guard suppresses post-stable nightlies `gh release list --limit 1` returns the most recent release by date, which could be a stable from `release.yml`. An explicit `workflow_dispatch` nightly right after a stable cut would be suppressed. Filter to prereleases only: gh release list --json tagName,isPrerelease \ --jq '[.[] | select(.isPrerelease)][0].tagName // empty' If no prerelease exists yet, the jq returns empty and the guard falls through naturally (LAST_SHA empty → condition false → nightly proceeds). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: retrigger checks (event dropped on previous push) * fix(release): make release.yml idempotent and dispatch reachable on re-run The previous design used a single `released` output (based on `after > before` tag count) to gate both `Create GitHub release` and `Dispatch npm publish`. On a re-run, all tags are already on the remote and `fetch-depth: 0` brings them down, so `pnpm changeset tag` adds nothing, `after == before`, `released=false`, and both steps are skipped — breaking the "re-run recovers cleanly" claim, especially the common case where the first run failed only at dispatch because `PUBLISHER_DISPATCH_TOKEN` was missing. Refactor into a `Determine release state` step that emits three independent signals: - `is_release_commit` — version-bump signal. Detected by comparing `packages/ao/package.json` version against its value in HEAD^. A Version Packages merge changes this; a regular commit does not. This is the filter that prevents the dispatch from firing on every commit to main (which all have `hasChangesets == 'false'`). - `tag_on_remote` — whether the `vX.Y.Z` tag exists at origin. - `release_exists` — whether the matching GitHub release exists. Each downstream step is gated on its own piece of state: - Tag push: `is_release_commit && !tag_on_remote` - Release create: `is_release_commit && !release_exists` - Dispatch: `is_release_commit` (always fires on a release commit) The dispatch fires unconditionally on a release commit, even when tag and release already exist on the remote. That guarantees recovery from the most common failure mode (first run succeeded everywhere except dispatch). The publisher must be idempotent against already- published versions for this to be safe — `pnpm changeset publish` already has this property since it skips packages whose current version is already on the registry. Update CONTRIBUTING.md → "Release Architecture": - Add "Idempotency contract for ao-publisher" section spelling out the no-op-on-already-published requirement. - Add "Recovery" section explaining that re-running the failed workflow is the canonical recovery path, plus a manual `gh api` fallback for cases where re-running isn't practical. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(canary): skip-guard anchors on tag parent, not the orphan snapshot The previous P1 fix added a "Commit snapshot version bumps" step before `pnpm changeset tag`, which moved the snapshot tag onto an orphan commit (the version-bump commit) rather than main HEAD. The skip-if-unchanged guard still compared `git rev-list -n 1 "$LAST_TAG"` against `GITHUB_SHA`, but `LAST_TAG` now resolves to the orphan snapshot commit's SHA, never the main SHA. The two could never be equal → `skip=true` was unreachable → every cron tick republished regardless of whether main had advanced. Use `${LAST_TAG}^` to anchor on the snapshot commit's first parent — which is the main HEAD at the time the previous nightly ran — and compare that against the current `GITHUB_SHA`. Now the guard fires correctly when main hasn't advanced since the last nightly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(release): single umbrella tag per release, drop per-package tags `pnpm changeset tag` creates one tag per publishable package (~27 here) on every release. At 5 nightlies/week × 52 weeks × 27 packages that's roughly 7 000 tags/year just from canary — pure decoration since `ao-publisher` only consumes the umbrella `vX.Y.Z` tag. The per- package tags also cause partial-recovery conflicts: `git push --tags` on a re-run trips over tags that were pushed by the prior run. Drop the `pnpm changeset tag` call from both workflows and replace `git push origin --tags` with `git push origin "v$version"`. Push exactly one umbrella tag per release. CONTRIBUTING.md → "Release Architecture" updated: - Flow diagram replaces "changeset tag → push" with "push vX.Y.Z tag" - New paragraph spells out the single-tag-per-release policy and why we skip `pnpm changeset tag`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(canary): workflow_dispatch bypasses skip-guard for recovery When a nightly pushes the tag but fails at `gh release create` or dispatch (e.g. `PUBLISHER_DISPATCH_TOKEN` missing), a re-run via `workflow_dispatch` was silently skipped: `LAST_TAG` resolves to the just-pushed nightly, `${LAST_TAG}^` equals `GITHUB_SHA`, and the binary skip guard fires before any of the downstream steps we want to retry. `release.yml` handles the equivalent case by gating each step on independent state. Canary has a single binary gate, so it needs a different escape hatch: `workflow_dispatch` always proceeds. The human trigger is itself the signal that we want to run regardless of whether the source SHA looks unchanged. Cron-driven runs keep the SHA-equality dedup so quiet stretches don't republish the same SHA on every tick. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(release): replace ao-publisher dispatch with AO cron poll model Remove the ao-publisher dispatch steps and PUBLISHER_DISPATCH_TOKEN from both release.yml and canary.yml. npm publishing is now handled by an AO cron job on a private server that polls GitHub releases and publishes when a new tag is ahead of the current npm version. Changes: - release.yml: remove dispatch step, keep tag + GitHub release - canary.yml: remove dispatch step, keep tag + GitHub prerelease - CONTRIBUTING.md: rewrite Release Architecture for two-stage model (public CI → GitHub release, private cron → npm publish) * fix(release): address review — remove env/id-token, scope git add, fix wording - Remove environment: release from both workflows (no npm publish here, could block on required reviewers) - Remove id-token: write permission (unused, no OIDC needed) - Scope git add in canary to package.json and .changeset/ only (avoid staging build artifacts) - Fix 'orphan commit' wording → 'snapshot commit' (not actually orphan) - Fix nightly version format 0.0.0-* → X.Y.Z-nightly-<sha> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: i-trytoohard[bot] <1484917231245856808@users.noreply.github.com> |
||
|---|---|---|
| .changeset | ||
| .cursor | ||
| .github | ||
| .husky | ||
| .issue-assets | ||
| ao-pr1483@2abb1c1e3d | ||
| artifacts | ||
| changelog | ||
| completions | ||
| docs | ||
| examples | ||
| handoff/pr-1466 | ||
| hermes-agent@27eeea0555 | ||
| libkrunfw@351d354b4b | ||
| openclaw-plugin | ||
| packages | ||
| schema | ||
| scripts | ||
| skills | ||
| tests/integration | ||
| website | ||
| .eslintignore | ||
| .gitignore | ||
| .gitleaks.toml | ||
| .npmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| ARCHITECTURE.md | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| DESIGN.md | ||
| LICENSE | ||
| README.md | ||
| SECURITY.md | ||
| SETUP.md | ||
| TROUBLESHOOTING.md | ||
| agent-orchestrator.yaml.example | ||
| eslint.config.js | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| tsconfig.base.json | ||
| tsconfig.node.json | ||
README.md
Agent Orchestrator — The Orchestration Layer for Parallel AI Agents
Spawn parallel AI coding agents, each in its own git worktree. Agents autonomously fix CI failures, address review comments, and open PRs — you supervise from one dashboard.
Agent Orchestrator manages fleets of AI coding agents working in parallel on your codebase. Each agent gets its own git worktree, its own branch, and its own PR. When CI fails, the agent fixes it. When reviewers leave comments, the agent addresses them. You only get pulled in when human judgment is needed.
Agent-agnostic (Claude Code, Codex, Aider) · Runtime-agnostic (tmux, ConPTY/process, Docker) · Tracker-agnostic (GitHub, Linear)
Quick Start
Prerequisites: Node.js 20+, Git 2.25+,
ghCLI, and:
- macOS / Linux: tmux — install via
brew install tmuxorsudo apt install tmux.- Windows: PowerShell 7+ recommended. tmux is not required — AO uses native ConPTY via the
runtime-processplugin (the default on Windows). SetAO_SHELL=bashif you have Git Bash and prefer it.
Install
npm install -g @aoagents/ao
Nightly builds (latest
main, daily Fri–Tue):npm install -g @aoagents/ao@nightly
Back to stable:npm install -g @aoagents/ao@latest
Permission denied? Install from source?
If npm install -g fails with EACCES, prefix with sudo or fix your npm permissions.
To install from source (for contributors):
git clone https://github.com/ComposioHQ/agent-orchestrator.git
cd agent-orchestrator && bash scripts/setup.sh
Zsh Completion
Generate the completion file from the installed CLI:
mkdir -p ~/.zsh/completions
ao completion zsh > ~/.zsh/completions/_ao
Then make sure the directory is on your fpath before compinit runs:
fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit
compinit
For Oh My Zsh, install the same generated file into a custom plugin directory and add ao to your plugin list:
mkdir -p "${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/ao"
ao completion zsh > "${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/ao/_ao"
If you are contributing from a source checkout, you can also symlink the repo copy at completions/_ao.
Start
Point it at any repo — it clones, configures, and launches the dashboard in one command:
ao start https://github.com/your-org/your-repo
Or from inside an existing local repo:
cd ~/your-project && ao start
That's it. The dashboard opens at http://localhost:3000 and the orchestrator agent starts managing your project.
Add more projects
ao start ~/path/to/another-repo
How It Works
- You start —
ao startlaunches the dashboard and an orchestrator agent - Orchestrator spawns workers — each issue gets its own agent in an isolated git worktree
- Agents work autonomously — they read code, write tests, create PRs
- Reactions handle feedback — CI failures and review comments are automatically routed back to the agent
- You review and merge — you only get pulled in when human judgment is needed
The orchestrator agent uses the AO CLI internally to manage sessions. You don't need to learn or use the CLI — the dashboard and orchestrator handle everything.
Configuration
ao start auto-generates agent-orchestrator.yaml with sensible defaults. You can edit it afterwards to customize behavior:
# agent-orchestrator.yaml
$schema: https://raw.githubusercontent.com/ComposioHQ/agent-orchestrator/main/schema/config.schema.json
# Runtime data is auto-derived under ~/.agent-orchestrator/{hash}-{projectId}/
port: 3000
defaults:
runtime: tmux # default on macOS / Linux; on Windows the default is `process` (ConPTY)
agent: claude-code
workspace: worktree
notifiers: [desktop]
projects:
my-app:
repo: owner/my-app
path: ~/my-app
defaultBranch: main
sessionPrefix: app
reactions:
ci-failed:
auto: true
action: send-to-agent
retries: 2
changes-requested:
auto: true
action: send-to-agent
escalateAfter: 30m
approved-and-green:
auto: false # flip to true for auto-merge
action: notify
CI fails → agent gets the logs and fixes it. Reviewer requests changes → agent addresses them. PR approved with green CI → you get a notification to merge.
Keep the $schema line so editors can autocomplete and validate against schema/config.schema.json.
See agent-orchestrator.yaml.example for the full reference, or run ao config-help for the complete schema.
Remote Access
AO keeps your Mac awake while running, so you can access the dashboard remotely (e.g., via Tailscale from your phone) without the machine going to sleep.
How it works: On macOS, AO automatically holds an idle-sleep prevention assertion using caffeinate. When AO exits, the assertion is released.
# agent-orchestrator.yaml
$schema: https://raw.githubusercontent.com/ComposioHQ/agent-orchestrator/main/schema/config.schema.json
power:
preventIdleSleep: true # Default on macOS; no-op on Linux and Windows
Set to false if you want to allow idle sleep while AO runs.
Lid-close limitation: macOS enforces lid-close sleep at the hardware level — no userspace assertion can override it. If you need remote access while traveling with the lid closed, use clamshell mode (external power + display + input device).
Linux / Windows: AO does not currently hold a wake assertion on these platforms. On Linux, idle-sleep behaviour is governed by your desktop environment / systemd-logind; configure that directly. On Windows, set the OS power plan if remote access matters while idle.
Plugin Architecture
Seven plugin slots. Lifecycle stays in core.
| Slot | Default | Alternatives |
|---|---|---|
| Runtime | tmux (macOS/Linux) / process (Windows) | process, docker |
| Agent | claude-code | codex, aider, cursor, opencode, kimicode |
| Workspace | worktree | clone |
| Tracker | github | linear, gitlab |
| SCM | github | gitlab |
| Notifier | desktop | slack, discord, composio, webhook, openclaw |
| Terminal | iterm2 | web |
All interfaces defined in packages/core/src/types.ts. A plugin implements one interface and exports a PluginModule. That's it.
Why Agent Orchestrator?
Running one AI agent in a terminal is easy. Running 30 across different issues, branches, and PRs is a coordination problem.
Without orchestration, you manually: create branches, start agents, check if they're stuck, read CI failures, forward review comments, track which PRs are ready to merge, clean up when done.
With Agent Orchestrator, you: ao start and walk away. The system handles isolation, feedback routing, and status tracking. You review PRs and make decisions — the rest is automated.
Documentation
| Doc | What it covers |
|---|---|
| Setup Guide | Detailed installation, configuration, and troubleshooting |
| CLI Reference | All ao commands (mostly used by the orchestrator agent) |
| Examples | Config templates (GitHub, Linear, multi-project, auto-merge) |
| Development Guide | Architecture, conventions, plugin pattern |
| Contributing | How to contribute, build plugins, PR process |
Development
pnpm install && pnpm build # Install and build all packages
pnpm test # Run tests (3,288 test cases)
pnpm dev # Start web dashboard dev server
See docs/DEVELOPMENT.md for code conventions and architecture details.
Contributing
Contributions welcome. The plugin system makes it straightforward to add support for new agents, runtimes, trackers, and notification channels. Every plugin is an implementation of a TypeScript interface — see CONTRIBUTING.md and the Development Guide for the pattern.
License
MIT