chore: add prettier config and CI auto-formatter (#166)
* chore: add prettier config and CI auto-formatter Adds .prettierrc and .prettierignore (config only, no local enforcement). Formatting runs in CI via the prettier.yml workflow: on every push to a non-main branch, Prettier rewrites changed files and commits the result back using GITHUB_TOKEN. Developers never need to run Prettier locally. Intentionally excludes husky/lint-staged — local pre-commit hooks are the wrong layer for a formatter that the whole team doesn't need installed. Also adds .envrc.local to .gitignore for personal local shell overrides. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Entire-Checkpoint: 27336650d2ee * chore: format with prettier [skip ci] --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
5071364f91
commit
5982051651
|
|
@ -0,0 +1,38 @@
|
||||||
|
name: Prettier
|
||||||
|
|
||||||
|
# Auto-formats the codebase on every push and commits the result back.
|
||||||
|
# Formatting is a CI concern — developers never need to run Prettier locally
|
||||||
|
# and formatted output never shows up as local uncommitted changes.
|
||||||
|
#
|
||||||
|
# GitHub Actions does not re-trigger workflows on commits made with GITHUB_TOKEN,
|
||||||
|
# so there is no feedback loop risk.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- main
|
||||||
|
- "entire/**"
|
||||||
|
- "worktree-**"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.ref }}
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Format with Prettier
|
||||||
|
run: npx --yes prettier@3 --write .
|
||||||
|
|
||||||
|
- name: Commit formatted files
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git diff --quiet && exit 0
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore: format with prettier [skip ci]"
|
||||||
|
git push
|
||||||
|
|
@ -46,3 +46,6 @@ session-events.jsonl.*
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# Personal local overrides (not for the team)
|
||||||
|
.envrc.local
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated — never hand-edit; regenerated by `npm run api` / sqlc / openapi-typescript
|
||||||
|
frontend/src/api/schema.ts
|
||||||
|
backend/internal/httpd/apispec/openapi.yaml
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
frontend/dist
|
||||||
|
frontend/dist-electron
|
||||||
|
frontend/release
|
||||||
|
frontend/test-results
|
||||||
|
frontend/playwright-report
|
||||||
|
|
||||||
|
# Lockfiles
|
||||||
|
package-lock.json
|
||||||
|
frontend/package-lock.json
|
||||||
|
|
||||||
|
# Go uses gofmt, not Prettier
|
||||||
|
backend/
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"useTabs": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"printWidth": 120,
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"semi": true,
|
||||||
|
"arrowParens": "always"
|
||||||
|
}
|
||||||
|
|
@ -92,21 +92,25 @@ For code entry points:
|
||||||
The daemon API is code-first. The OpenAPI spec and frontend TypeScript types are generated artifacts — edit the source, then regenerate.
|
The daemon API is code-first. The OpenAPI spec and frontend TypeScript types are generated artifacts — edit the source, then regenerate.
|
||||||
|
|
||||||
**Source files to edit:**
|
**Source files to edit:**
|
||||||
|
|
||||||
- `backend/internal/httpd/controllers/dto.go` — request/response shapes.
|
- `backend/internal/httpd/controllers/dto.go` — request/response shapes.
|
||||||
- `backend/internal/httpd/apispec/specgen/build.go` — operation registry; add a `schemaNames` entry for any new named type.
|
- `backend/internal/httpd/apispec/specgen/build.go` — operation registry; add a `schemaNames` entry for any new named type.
|
||||||
|
|
||||||
**Regenerate after editing:**
|
**Regenerate after editing:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run api # runs api:spec then api:ts in sequence
|
npm run api # runs api:spec then api:ts in sequence
|
||||||
```
|
```
|
||||||
|
|
||||||
This is equivalent to running:
|
This is equivalent to running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run api:spec # cd backend && go generate ./internal/httpd/apispec/...
|
npm run api:spec # cd backend && go generate ./internal/httpd/apispec/...
|
||||||
npm run api:ts # npx openapi-typescript@7.4.4 backend/internal/httpd/apispec/openapi.yaml -o frontend/src/api/schema.ts
|
npm run api:ts # npx openapi-typescript@7.4.4 backend/internal/httpd/apispec/openapi.yaml -o frontend/src/api/schema.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
**Verify:**
|
**Verify:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd backend && go test ./internal/httpd/... # spec drift + route/spec parity tests (does not cover schema.ts — that is checked by the api-drift CI job)
|
cd backend && go test ./internal/httpd/... # spec drift + route/spec parity tests (does not cover schema.ts — that is checked by the api-drift CI job)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ route. Run `ao <command> --help` for the authoritative flag shape; the table
|
||||||
below groups what's on `main` today.
|
below groups what's on `main` today.
|
||||||
|
|
||||||
| Lane | Command | Purpose |
|
| Lane | Command | Purpose |
|
||||||
|---|---|---|
|
| ------------ | ------------------------------------ | ------------------------------------------------------------ |
|
||||||
| Daemon | `ao start` | Start the daemon in the background and wait for `/readyz`. |
|
| Daemon | `ao start` | Start the daemon in the background and wait for `/readyz`. |
|
||||||
| Daemon | `ao stop` | Graceful shutdown via loopback `POST /shutdown`. |
|
| Daemon | `ao stop` | Graceful shutdown via loopback `POST /shutdown`. |
|
||||||
| Daemon | `ao status` | Report PID/port/health/readiness from `running.json`. |
|
| Daemon | `ao status` | Report PID/port/health/readiness from `running.json`. |
|
||||||
|
|
@ -96,7 +96,7 @@ host is hard-coded to `127.0.0.1` — the daemon has no auth, CORS, or TLS, and
|
||||||
exposing it beyond loopback would be a security regression.
|
exposing it beyond loopback would be a security regression.
|
||||||
|
|
||||||
| Var | Default | Purpose |
|
| Var | Default | Purpose |
|
||||||
|---|---|---|
|
| --------------------- | ------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||||
| `AO_PORT` | `3001` | Bind port; daemon fails fast if taken. |
|
| `AO_PORT` | `3001` | Bind port; daemon fails fast if taken. |
|
||||||
| `AO_REQUEST_TIMEOUT` | `60s` | Per-request timeout (Go duration). |
|
| `AO_REQUEST_TIMEOUT` | `60s` | Per-request timeout (Go duration). |
|
||||||
| `AO_SHUTDOWN_TIMEOUT` | `10s` | Graceful-shutdown hard cap. |
|
| `AO_SHUTDOWN_TIMEOUT` | `10s` | Graceful-shutdown hard cap. |
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ Start with [architecture.md](architecture.md) for the current backend model and
|
||||||
## Reference docs
|
## Reference docs
|
||||||
|
|
||||||
| Doc | What it covers |
|
| Doc | What it covers |
|
||||||
|-----|----------------|
|
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [architecture.md](architecture.md) | Current backend model, package layout, status derivation, persistence/CDC, and load-bearing rules. |
|
| [architecture.md](architecture.md) | Current backend model, package layout, status derivation, persistence/CDC, and load-bearing rules. |
|
||||||
| [backend-code-structure.md](backend-code-structure.md) | Package ownership rules for the Go backend: domain, services, ports, adapters, storage, HTTP, CLI, and daemon wiring. |
|
| [backend-code-structure.md](backend-code-structure.md) | Package ownership rules for the Go backend: domain, services, ports, adapters, storage, HTTP, CLI, and daemon wiring. |
|
||||||
| [cli/README.md](cli/README.md) | CLI commands and daemon control surface. |
|
| [cli/README.md](cli/README.md) | CLI commands and daemon control surface. |
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,6 @@ The workspace adapter prefers:
|
||||||
|
|
||||||
Hook metadata changes publish `session.updated`. The frontend listens to `session.created`, `session.terminated`, and `session.updated` and invalidates the workspace query.
|
Hook metadata changes publish `session.updated`. The frontend listens to `session.created`, `session.terminated`, and `session.updated` and invalidates the workspace query.
|
||||||
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
Agent adapter behavior:
|
Agent adapter behavior:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ call runtime, workspace, tracker, or agent adapters in-process.
|
||||||
## Current commands
|
## Current commands
|
||||||
|
|
||||||
| Command | Purpose |
|
| Command | Purpose |
|
||||||
|---|---|
|
| ----------------------------- | ------------------------------------------------------------------------------------------- |
|
||||||
| `ao start` | Start the daemon in the background and wait for `/readyz`. |
|
| `ao start` | Start the daemon in the background and wait for `/readyz`. |
|
||||||
| `ao status` | Report daemon state from `running.json`, process liveness, `/healthz`, and `/readyz`. |
|
| `ao status` | Report daemon state from `running.json`, process liveness, `/healthz`, and `/readyz`. |
|
||||||
| `ao status --json` | Emit the same daemon state as machine-readable JSON. |
|
| `ao status --json` | Emit the same daemon state as machine-readable JSON. |
|
||||||
|
|
@ -26,7 +26,7 @@ call runtime, workspace, tracker, or agent adapters in-process.
|
||||||
The CLI and daemon share the same environment-driven config:
|
The CLI and daemon share the same environment-driven config:
|
||||||
|
|
||||||
| Var | Default | Purpose |
|
| Var | Default | Purpose |
|
||||||
|---|---|---|
|
| --------------------- | ------------------------------------------------- | ---------------------- |
|
||||||
| `AO_PORT` | `3001` | Loopback daemon port. |
|
| `AO_PORT` | `3001` | Loopback daemon port. |
|
||||||
| `AO_RUN_FILE` | `<UserConfigDir>/agent-orchestrator/running.json` | PID/port handshake. |
|
| `AO_RUN_FILE` | `<UserConfigDir>/agent-orchestrator/running.json` | PID/port handshake. |
|
||||||
| `AO_DATA_DIR` | `<UserConfigDir>/agent-orchestrator/data` | SQLite data directory. |
|
| `AO_DATA_DIR` | `<UserConfigDir>/agent-orchestrator/data` | SQLite data directory. |
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ rather than an escape-hatch map.
|
||||||
## Field catalog (legacy `projects.<id>`) and target home
|
## Field catalog (legacy `projects.<id>`) and target home
|
||||||
|
|
||||||
| YAML field | Type | Storage today | Target |
|
| YAML field | Type | Storage today | Target |
|
||||||
|---|---|---|---|
|
| --------------------------------- | ---------------------- | ----------------------------------- | ---------------------------------------------------- |
|
||||||
| `name` | string | `projects.display_name` | done |
|
| `name` | string | `projects.display_name` | done |
|
||||||
| `repo` | string | `projects.repo_origin_url` | done |
|
| `repo` | string | `projects.repo_origin_url` | done |
|
||||||
| `path` | string | `projects.path` | done |
|
| `path` | string | `projects.path` | done |
|
||||||
|
|
@ -112,7 +112,7 @@ agent adapter.
|
||||||
|
|
||||||
## Sequencing (one slice per PR)
|
## Sequencing (one slice per PR)
|
||||||
|
|
||||||
1. **agentConfig (typed)** — *this PR*. Establishes the typed+validated+surfaced
|
1. **agentConfig (typed)** — _this PR_. Establishes the typed+validated+surfaced
|
||||||
pattern end to end.
|
pattern end to end.
|
||||||
2. **Project identity scalars** — `default_branch`, `session_prefix` (stop
|
2. **Project identity scalars** — `default_branch`, `session_prefix` (stop
|
||||||
hardcoding/deriving them).
|
hardcoding/deriving them).
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ invariants.
|
||||||
## Accepted stack
|
## Accepted stack
|
||||||
|
|
||||||
| Area | Decision | Status | Rationale |
|
| Area | Decision | Status | Rationale |
|
||||||
|------|----------|--------|-----------|
|
| ------------------ | ----------------------------------------------------------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| Backend language | Go 1.25.7 | Implemented | Matches `backend/go.mod`; small daemon, strong stdlib, easy local distribution. |
|
| Backend language | Go 1.25.7 | Implemented | Matches `backend/go.mod`; small daemon, strong stdlib, easy local distribution. |
|
||||||
| Backend core | Go stdlib | Implemented | Domain, lifecycle, session, and adapter contracts should stay dependency-light. |
|
| Backend core | Go stdlib | Implemented | Domain, lifecycle, session, and adapter contracts should stay dependency-light. |
|
||||||
| Frontend shell | Electron + TypeScript | Implemented | Local desktop control plane paired with the daemon. |
|
| Frontend shell | Electron + TypeScript | Implemented | Local desktop control plane paired with the daemon. |
|
||||||
|
|
@ -71,7 +71,7 @@ config surface appears.
|
||||||
## Explicitly avoided for V1
|
## Explicitly avoided for V1
|
||||||
|
|
||||||
| Avoid | Reason |
|
| Avoid | Reason |
|
||||||
|-------|--------|
|
| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||||
| GORM | AO needs explicit transactional SQL and CDC-triggered writes. |
|
| GORM | AO needs explicit transactional SQL and CDC-triggered writes. |
|
||||||
| Gin/Fiber | `net/http` + `chi` is enough for a local daemon API. |
|
| Gin/Fiber | `net/http` + `chi` is enough for a local daemon API. |
|
||||||
| `go-git` as the primary Git engine | AO should match installed Git behavior, credentials, hooks, LFS, submodules, and user config. |
|
| `go-git` as the primary Git engine | AO should match installed Git behavior, credentials, hooks, LFS, submodules, and user config. |
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import {
|
import { DocsPage, DocsBody, DocsTitle, DocsDescription } from "fumadocs-ui/page";
|
||||||
DocsPage,
|
|
||||||
DocsBody,
|
|
||||||
DocsTitle,
|
|
||||||
DocsDescription,
|
|
||||||
} from "fumadocs-ui/page";
|
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { source } from "@/lib/source";
|
import { source } from "@/lib/source";
|
||||||
import { getMDXComponents } from "@/components/docs/mdx-components";
|
import { getMDXComponents } from "@/components/docs/mdx-components";
|
||||||
|
|
@ -55,9 +50,7 @@ export async function generateStaticParams() {
|
||||||
return source.generateParams();
|
return source.generateParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
||||||
params,
|
|
||||||
}: PageProps): Promise<Metadata> {
|
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
const page = source.getPage(slug);
|
const page = source.getPage(slug);
|
||||||
if (!page) {
|
if (!page) {
|
||||||
|
|
|
||||||
|
|
@ -199,11 +199,7 @@
|
||||||
#nd-docs-layout code,
|
#nd-docs-layout code,
|
||||||
#nd-docs-layout kbd,
|
#nd-docs-layout kbd,
|
||||||
#nd-docs-layout pre {
|
#nd-docs-layout pre {
|
||||||
font-family:
|
font-family: var(--font-jetbrains-mono), ui-monospace, "SFMono-Regular", monospace;
|
||||||
var(--font-jetbrains-mono),
|
|
||||||
ui-monospace,
|
|
||||||
"SFMono-Regular",
|
|
||||||
monospace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sidebar — smaller text like dashboard */
|
/* Sidebar — smaller text like dashboard */
|
||||||
|
|
@ -512,7 +508,10 @@ pre.shiki code {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: border-color 0.15s ease, color 0.15s ease, opacity 0.15s ease;
|
transition:
|
||||||
|
border-color 0.15s ease,
|
||||||
|
color 0.15s ease,
|
||||||
|
opacity 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nd-docs-layout .docs-missing-primary {
|
#nd-docs-layout .docs-missing-primary {
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,9 @@ const links: LinkItemType[] = [
|
||||||
async function GitHubStars() {
|
async function GitHubStars() {
|
||||||
let stars: string | null = null;
|
let stars: string | null = null;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch("https://api.github.com/repos/ComposioHQ/agent-orchestrator", {
|
||||||
"https://api.github.com/repos/ComposioHQ/agent-orchestrator",
|
next: { revalidate: 3600 },
|
||||||
{ next: { revalidate: 3600 } },
|
});
|
||||||
);
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const count = data.stargazers_count as number;
|
const count = data.stargazers_count as number;
|
||||||
|
|
@ -66,14 +65,7 @@ async function GitHubStars() {
|
||||||
|
|
||||||
return stars ? (
|
return stars ? (
|
||||||
<span className="inline-flex items-center gap-1 text-[var(--color-text-muted)]">
|
<span className="inline-flex items-center gap-1 text-[var(--color-text-muted)]">
|
||||||
<svg
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
width="12"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
|
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
|
||||||
</svg>
|
</svg>
|
||||||
{stars}
|
{stars}
|
||||||
|
|
@ -83,24 +75,14 @@ async function GitHubStars() {
|
||||||
|
|
||||||
export default function Layout({ children }: { children: ReactNode }) {
|
export default function Layout({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<RootProvider
|
<RootProvider theme={{ enabled: true, defaultTheme: "dark" }} search={{ options: { type: "static" } }}>
|
||||||
theme={{ enabled: true, defaultTheme: "dark" }}
|
|
||||||
search={{ options: { type: "static" } }}
|
|
||||||
>
|
|
||||||
<DocsLayout
|
<DocsLayout
|
||||||
tree={source.pageTree}
|
tree={source.pageTree}
|
||||||
links={links}
|
links={links}
|
||||||
nav={{
|
nav={{
|
||||||
title: (
|
title: (
|
||||||
<span className="flex items-center gap-2 font-semibold">
|
<span className="flex items-center gap-2 font-semibold">
|
||||||
<img
|
<img src="/ao-logo.svg" alt="" aria-hidden="true" width={22} height={22} className="h-[22px] w-[22px]" />
|
||||||
src="/ao-logo.svg"
|
|
||||||
alt=""
|
|
||||||
aria-hidden="true"
|
|
||||||
width={22}
|
|
||||||
height={22}
|
|
||||||
className="h-[22px] w-[22px]"
|
|
||||||
/>
|
|
||||||
<span className="text-[var(--color-text-primary)]">AO</span>
|
<span className="text-[var(--color-text-primary)]">AO</span>
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,19 @@ export default async function LandingPage() {
|
||||||
<LandingAbout />
|
<LandingAbout />
|
||||||
<LandingAgentsBar />
|
<LandingAgentsBar />
|
||||||
<LandingFeatures />
|
<LandingFeatures />
|
||||||
<div id="workflow"><LandingWorkflow /></div>
|
<div id="workflow">
|
||||||
<div id="usecases"><LandingUseCases /></div>
|
<LandingWorkflow />
|
||||||
|
</div>
|
||||||
|
<div id="usecases">
|
||||||
|
<LandingUseCases />
|
||||||
|
</div>
|
||||||
<LandingHowItWorks />
|
<LandingHowItWorks />
|
||||||
<LandingVideo />
|
<LandingVideo />
|
||||||
<LandingStats stats={githubStats} />
|
<LandingStats stats={githubStats} />
|
||||||
<LandingTestimonials />
|
<LandingTestimonials />
|
||||||
<div id="quickstart"><LandingQuickStart /></div>
|
<div id="quickstart">
|
||||||
|
<LandingQuickStart />
|
||||||
|
</div>
|
||||||
<LandingCTA />
|
<LandingCTA />
|
||||||
<footer className="py-12 px-8 text-center text-[var(--landing-muted)] opacity-30 text-[0.8125rem] border-t border-white/[0.04]">
|
<footer className="py-12 px-8 text-center text-[var(--landing-muted)] opacity-30 text-[0.8125rem] border-t border-white/[0.04]">
|
||||||
MIT Licensed · Open Source
|
MIT Licensed · Open Source
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,16 @@ export function LandingAbout() {
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 items-start">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 items-start">
|
||||||
<p className="text-[0.9375rem] text-[var(--landing-muted)] leading-[1.8] max-w-[28rem]">
|
<p className="text-[0.9375rem] text-[var(--landing-muted)] leading-[1.8] max-w-[28rem]">
|
||||||
Agent Orchestrator replaces that with one YAML file. Point it at
|
Agent Orchestrator replaces that with one YAML file. Point it at your GitHub issues, pick your agents, and
|
||||||
your GitHub issues, pick your agents, and walk away. Each agent
|
walk away. Each agent spawns in its own git worktree, creates PRs, fixes CI failures, addresses review
|
||||||
spawns in its own git worktree, creates PRs, fixes CI failures,
|
comments, and moves toward merge. If you are new, start with the{" "}
|
||||||
addresses review comments, and moves toward merge. If you are new, start with the <a href="/docs/" className="underline decoration-[var(--landing-border-default)] underline-offset-4 hover:text-white">docs quickstart and configuration guides</a>.
|
<a
|
||||||
|
href="/docs/"
|
||||||
|
className="underline decoration-[var(--landing-border-default)] underline-offset-4 hover:text-white"
|
||||||
|
>
|
||||||
|
docs quickstart and configuration guides
|
||||||
|
</a>
|
||||||
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Config preview — show how simple setup is */}
|
{/* Config preview — show how simple setup is */}
|
||||||
|
|
|
||||||
|
|
@ -35,14 +35,8 @@ export function LandingAgentsBar() {
|
||||||
<div className="flex items-center justify-center gap-6 flex-wrap">
|
<div className="flex items-center justify-center gap-6 flex-wrap">
|
||||||
{agents.map((agent) => (
|
{agents.map((agent) => (
|
||||||
<div key={agent.name} className="flex flex-col items-center gap-2">
|
<div key={agent.name} className="flex flex-col items-center gap-2">
|
||||||
<img
|
<img src={agent.src} alt={agent.alt} className="w-8 h-8 rounded-md object-contain" />
|
||||||
src={agent.src}
|
<div className="text-[0.6875rem] font-mono text-[var(--landing-muted)] opacity-50">{agent.name}</div>
|
||||||
alt={agent.alt}
|
|
||||||
className="w-8 h-8 rounded-md object-contain"
|
|
||||||
/>
|
|
||||||
<div className="text-[0.6875rem] font-mono text-[var(--landing-muted)] opacity-50">
|
|
||||||
{agent.name}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,11 @@ export function LandingDifferentiators() {
|
||||||
Why Agent Orchestrator
|
Why Agent Orchestrator
|
||||||
</div>
|
</div>
|
||||||
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.1] tracking-[-1.5px] mb-6 max-w-[42rem]">
|
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.1] tracking-[-1.5px] mb-6 max-w-[42rem]">
|
||||||
The only{" "}
|
The only <em className="italic text-[var(--landing-muted)]">open-source, web-based</em> agent orchestrator
|
||||||
<em className="italic text-[var(--landing-muted)]">open-source, web-based</em>{" "}
|
|
||||||
agent orchestrator
|
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-[0.9375rem] text-[var(--landing-muted)] leading-[1.7] max-w-[36rem] mb-12">
|
<p className="text-[0.9375rem] text-[var(--landing-muted)] leading-[1.7] max-w-[36rem] mb-12">
|
||||||
Conductor, T3 Code, and Codex App are native Mac apps. AO runs in
|
Conductor, T3 Code, and Codex App are native Mac apps. AO runs in your browser, works on any OS, and you can
|
||||||
your browser, works on any OS, and you can self-host or extend it.
|
self-host or extend it.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="landing-reveal landing-card rounded-2xl overflow-hidden">
|
<div className="landing-reveal landing-card rounded-2xl overflow-hidden">
|
||||||
|
|
@ -45,12 +43,8 @@ export function LandingDifferentiators() {
|
||||||
key={row.feature}
|
key={row.feature}
|
||||||
className={i < rows.length - 1 ? "border-b border-[var(--landing-border-subtle)]" : ""}
|
className={i < rows.length - 1 ? "border-b border-[var(--landing-border-subtle)]" : ""}
|
||||||
>
|
>
|
||||||
<td className="px-6 py-3.5 text-[0.8125rem] text-[var(--landing-fg)]/80">
|
<td className="px-6 py-3.5 text-[0.8125rem] text-[var(--landing-fg)]/80">{row.feature}</td>
|
||||||
{row.feature}
|
<td className="px-6 py-3.5 text-center text-[rgba(134,239,172,0.8)]">✓</td>
|
||||||
</td>
|
|
||||||
<td className="px-6 py-3.5 text-center text-[rgba(134,239,172,0.8)]">
|
|
||||||
✓
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-3.5 text-center text-[0.75rem] text-[var(--landing-muted)] opacity-40">
|
<td className="px-6 py-3.5 text-center text-[0.75rem] text-[var(--landing-muted)] opacity-40">
|
||||||
{row.others}
|
{row.others}
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -145,21 +145,15 @@ export function LandingFeatures() {
|
||||||
marginBottom: "1.5rem",
|
marginBottom: "1.5rem",
|
||||||
transformOrigin: "center top",
|
transformOrigin: "center top",
|
||||||
transition: "transform 0.4s ease, opacity 0.4s ease, border-color 0.2s ease",
|
transition: "transform 0.4s ease, opacity 0.4s ease, border-color 0.2s ease",
|
||||||
...(stack
|
...(stack ? { position: "sticky", top: `${BASE_TOP + i * STACK_GAP}px`, zIndex: i + 1 } : null),
|
||||||
? { position: "sticky", top: `${BASE_TOP + i * STACK_GAP}px`, zIndex: i + 1 }
|
|
||||||
: null),
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-mono text-xs tracking-[0.1em] text-[var(--landing-muted)] opacity-50 mb-4">
|
<div className="font-mono text-xs tracking-[0.1em] text-[var(--landing-muted)] opacity-50 mb-4">
|
||||||
{f.n}
|
{f.n}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-sans font-[680] tracking-tight text-[1.375rem] mb-4">
|
<h3 className="font-sans font-[680] tracking-tight text-[1.375rem] mb-4">{f.title}</h3>
|
||||||
{f.title}
|
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.7] max-w-[28rem]">{f.desc}</p>
|
||||||
</h3>
|
|
||||||
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.7] max-w-[28rem]">
|
|
||||||
{f.desc}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<FeatureDemo kind={f.demo} />
|
<FeatureDemo kind={f.demo} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -196,13 +190,8 @@ function ParallelBack() {
|
||||||
className="bg-[rgba(255,240,220,0.035)] border border-[var(--landing-border-subtle)] rounded-xl p-3 flex flex-col"
|
className="bg-[rgba(255,240,220,0.035)] border border-[var(--landing-border-subtle)] rounded-xl p-3 flex flex-col"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1.5 mb-1">
|
<div className="flex items-center gap-1.5 mb-1">
|
||||||
<span
|
<span className="w-1.5 h-1.5 rounded-full shrink-0" style={{ background: a.color }} />
|
||||||
className="w-1.5 h-1.5 rounded-full shrink-0"
|
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/85 truncate">{a.name}</span>
|
||||||
style={{ background: a.color }}
|
|
||||||
/>
|
|
||||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/85 truncate">
|
|
||||||
{a.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-[0.625rem] text-[var(--landing-muted)] opacity-65 mb-auto truncate">
|
<div className="font-mono text-[0.625rem] text-[var(--landing-muted)] opacity-65 mb-auto truncate">
|
||||||
{a.task}
|
{a.task}
|
||||||
|
|
@ -243,13 +232,8 @@ function ParallelFront() {
|
||||||
key={a.name}
|
key={a.name}
|
||||||
className="flex items-center gap-2 bg-[rgba(255,240,220,0.04)] border border-[var(--landing-border-subtle)] rounded-md px-2.5 py-1.5"
|
className="flex items-center gap-2 bg-[rgba(255,240,220,0.04)] border border-[var(--landing-border-subtle)] rounded-md px-2.5 py-1.5"
|
||||||
>
|
>
|
||||||
<span
|
<span className="w-1.5 h-1.5 rounded-full shrink-0" style={{ background: a.color }} />
|
||||||
className="w-1.5 h-1.5 rounded-full shrink-0"
|
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/85 truncate">{a.name}</span>
|
||||||
style={{ background: a.color }}
|
|
||||||
/>
|
|
||||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/85 truncate">
|
|
||||||
{a.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -284,9 +268,7 @@ function RecoveryBack() {
|
||||||
<div className="p-5 h-full flex flex-col font-mono text-[0.6875rem]">
|
<div className="p-5 h-full flex flex-col font-mono text-[0.6875rem]">
|
||||||
<div className="flex items-center justify-between mb-3 pb-3 border-b border-[var(--landing-border-subtle)]">
|
<div className="flex items-center justify-between mb-3 pb-3 border-b border-[var(--landing-border-subtle)]">
|
||||||
<span className="text-[var(--landing-fg)]/80">PR #312 · feat/user-auth</span>
|
<span className="text-[var(--landing-fg)]/80">PR #312 · feat/user-auth</span>
|
||||||
<span className="text-[0.5625rem] uppercase tracking-[0.1em] text-[var(--landing-muted-dim)]">
|
<span className="text-[0.5625rem] uppercase tracking-[0.1em] text-[var(--landing-muted-dim)]">healing</span>
|
||||||
healing
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 space-y-1.5 overflow-hidden">
|
<div className="flex-1 space-y-1.5 overflow-hidden">
|
||||||
{visible.map((s, i) => {
|
{visible.map((s, i) => {
|
||||||
|
|
@ -304,9 +286,7 @@ function RecoveryBack() {
|
||||||
key={`${i}-${s.text}`}
|
key={`${i}-${s.text}`}
|
||||||
className={`flex items-baseline gap-2.5 ${isLast ? "landing-stream-line" : ""}`}
|
className={`flex items-baseline gap-2.5 ${isLast ? "landing-stream-line" : ""}`}
|
||||||
>
|
>
|
||||||
<span className="text-[var(--landing-muted-dim)] opacity-50 w-9 shrink-0">
|
<span className="text-[var(--landing-muted-dim)] opacity-50 w-9 shrink-0">{s.time}</span>
|
||||||
{s.time}
|
|
||||||
</span>
|
|
||||||
<span className={`${color} truncate`}>{s.text}</span>
|
<span className={`${color} truncate`}>{s.text}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -320,16 +300,12 @@ function RecoveryFront() {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-2 gap-2.5 p-5 w-full h-full items-stretch">
|
<div className="grid grid-cols-2 gap-2.5 p-5 w-full h-full items-stretch">
|
||||||
<div className="bg-[rgba(248,113,113,0.05)] border border-[rgba(248,113,113,0.18)] rounded-xl p-3 flex flex-col items-center justify-center gap-1">
|
<div className="bg-[rgba(248,113,113,0.05)] border border-[rgba(248,113,113,0.18)] rounded-xl p-3 flex flex-col items-center justify-center gap-1">
|
||||||
<span className="font-mono text-[0.5rem] tracking-[0.12em] uppercase text-[rgba(248,113,113,0.7)]">
|
<span className="font-mono text-[0.5rem] tracking-[0.12em] uppercase text-[rgba(248,113,113,0.7)]">before</span>
|
||||||
before
|
|
||||||
</span>
|
|
||||||
<span className="text-[1.75rem] leading-none text-[rgba(248,113,113,0.85)]">✗</span>
|
<span className="text-[1.75rem] leading-none text-[rgba(248,113,113,0.85)]">✗</span>
|
||||||
<span className="font-mono text-[0.625rem] text-[var(--landing-fg)]/70">12/48</span>
|
<span className="font-mono text-[0.625rem] text-[var(--landing-fg)]/70">12/48</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-[rgba(134,239,172,0.05)] border border-[rgba(134,239,172,0.2)] rounded-xl p-3 flex flex-col items-center justify-center gap-1">
|
<div className="bg-[rgba(134,239,172,0.05)] border border-[rgba(134,239,172,0.2)] rounded-xl p-3 flex flex-col items-center justify-center gap-1">
|
||||||
<span className="font-mono text-[0.5rem] tracking-[0.12em] uppercase text-[rgba(134,239,172,0.7)]">
|
<span className="font-mono text-[0.5rem] tracking-[0.12em] uppercase text-[rgba(134,239,172,0.7)]">after</span>
|
||||||
after
|
|
||||||
</span>
|
|
||||||
<span className="text-[1.75rem] leading-none text-[rgba(134,239,172,0.85)]">✓</span>
|
<span className="text-[1.75rem] leading-none text-[rgba(134,239,172,0.85)]">✓</span>
|
||||||
<span className="font-mono text-[0.625rem] text-[var(--landing-fg)]/70">48/48</span>
|
<span className="font-mono text-[0.625rem] text-[var(--landing-fg)]/70">48/48</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -357,9 +333,7 @@ function PluginsBack() {
|
||||||
return (
|
return (
|
||||||
<div className="p-5 h-full flex flex-col">
|
<div className="p-5 h-full flex flex-col">
|
||||||
<div className="flex items-center justify-between mb-3 pb-3 border-b border-[var(--landing-border-subtle)]">
|
<div className="flex items-center justify-between mb-3 pb-3 border-b border-[var(--landing-border-subtle)]">
|
||||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/80">
|
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/80">agent-orchestrator.yaml</span>
|
||||||
agent-orchestrator.yaml
|
|
||||||
</span>
|
|
||||||
<span className="font-mono text-[0.5625rem] tracking-[0.1em] uppercase text-[var(--landing-muted-dim)]">
|
<span className="font-mono text-[0.5625rem] tracking-[0.1em] uppercase text-[var(--landing-muted-dim)]">
|
||||||
7 slots
|
7 slots
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -369,9 +343,7 @@ function PluginsBack() {
|
||||||
const val = s.values[(tick + i) % s.values.length];
|
const val = s.values[(tick + i) % s.values.length];
|
||||||
return (
|
return (
|
||||||
<div key={s.slot} className="flex items-center gap-3">
|
<div key={s.slot} className="flex items-center gap-3">
|
||||||
<span className="text-[var(--landing-muted-dim)] w-[4.5rem] shrink-0">
|
<span className="text-[var(--landing-muted-dim)] w-[4.5rem] shrink-0">{s.slot}:</span>
|
||||||
{s.slot}:
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
key={val}
|
key={val}
|
||||||
className="landing-chip-swap inline-block px-2 py-[1px] rounded-md bg-[rgba(255,240,220,0.05)] text-[var(--landing-fg)]/85 border border-[var(--landing-border-subtle)]"
|
className="landing-chip-swap inline-block px-2 py-[1px] rounded-md bg-[rgba(255,240,220,0.05)] text-[var(--landing-fg)]/85 border border-[var(--landing-border-subtle)]"
|
||||||
|
|
@ -448,9 +420,7 @@ function DashboardBack() {
|
||||||
return prev.map((c) => ({ ...c, col: 0 as 0 | 1 | 2 }));
|
return prev.map((c) => ({ ...c, col: 0 as 0 | 1 | 2 }));
|
||||||
}
|
}
|
||||||
const oldest = advanceable[0];
|
const oldest = advanceable[0];
|
||||||
return prev.map((c) =>
|
return prev.map((c) => (c.id === oldest.id ? { ...c, col: (c.col + 1) as 0 | 1 | 2 } : c));
|
||||||
c.id === oldest.id ? { ...c, col: (c.col + 1) as 0 | 1 | 2 } : c,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}, 2400);
|
}, 2400);
|
||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
|
|
@ -459,9 +429,7 @@ function DashboardBack() {
|
||||||
return (
|
return (
|
||||||
<div className="p-5 h-full flex flex-col">
|
<div className="p-5 h-full flex flex-col">
|
||||||
<div className="flex items-center justify-between mb-3 pb-3 border-b border-[var(--landing-border-subtle)]">
|
<div className="flex items-center justify-between mb-3 pb-3 border-b border-[var(--landing-border-subtle)]">
|
||||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/80">
|
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/80">my-saas-app · 4 sessions</span>
|
||||||
my-saas-app · 4 sessions
|
|
||||||
</span>
|
|
||||||
<span className="font-mono text-[0.5625rem] tracking-[0.1em] uppercase text-[var(--landing-muted-dim)] flex items-center gap-1.5">
|
<span className="font-mono text-[0.5625rem] tracking-[0.1em] uppercase text-[var(--landing-muted-dim)] flex items-center gap-1.5">
|
||||||
<span className="w-1 h-1 rounded-full bg-[rgba(134,239,172,0.7)] landing-sse-pulse" />
|
<span className="w-1 h-1 rounded-full bg-[rgba(134,239,172,0.7)] landing-sse-pulse" />
|
||||||
sse
|
sse
|
||||||
|
|
@ -482,13 +450,8 @@ function DashboardBack() {
|
||||||
>
|
>
|
||||||
<div className="text-[var(--landing-fg)]/85 truncate mb-1">{c.title}</div>
|
<div className="text-[var(--landing-fg)]/85 truncate mb-1">{c.title}</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span
|
<span className="w-1 h-1 rounded-full shrink-0" style={{ background: c.color }} />
|
||||||
className="w-1 h-1 rounded-full shrink-0"
|
<span className="font-mono text-[0.5rem] text-[var(--landing-muted-dim)] truncate">{c.agent}</span>
|
||||||
style={{ background: c.color }}
|
|
||||||
/>
|
|
||||||
<span className="font-mono text-[0.5rem] text-[var(--landing-muted-dim)] truncate">
|
|
||||||
{c.agent}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -546,12 +509,9 @@ function DashboardFront() {
|
||||||
{stream.map((l) => (
|
{stream.map((l) => (
|
||||||
<div
|
<div
|
||||||
key={l.id}
|
key={l.id}
|
||||||
className={`truncate transition-opacity duration-200 ${
|
className={`truncate transition-opacity duration-200 ${l.exiting ? "opacity-0" : "landing-stream-line"}`}
|
||||||
l.exiting ? "opacity-0" : "landing-stream-line"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<span className="text-[rgba(134,239,172,0.7)]">✓</span>{" "}
|
<span className="text-[rgba(134,239,172,0.7)]">✓</span> <span className="opacity-70">{l.text}</span>
|
||||||
<span className="opacity-70">{l.text}</span>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,8 @@ export function LandingHero({ starsLabel }: LandingHeroProps) {
|
||||||
<span className="text-[var(--landing-muted)]">One dashboard.</span>
|
<span className="text-[var(--landing-muted)]">One dashboard.</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="landing-fade-rise-d1 text-[var(--landing-muted)] text-[0.9375rem] max-w-[38rem] mt-6 leading-[1.7]">
|
<p className="landing-fade-rise-d1 text-[var(--landing-muted)] text-[0.9375rem] max-w-[38rem] mt-6 leading-[1.7]">
|
||||||
Agent Orchestrator spawns Claude Code, Codex, Cursor, Aider, and OpenCode
|
Agent Orchestrator spawns Claude Code, Codex, Cursor, Aider, and OpenCode in isolated git worktrees. Each
|
||||||
in isolated git worktrees. Each agent gets its own branch, creates PRs,
|
agent gets its own branch, creates PRs, fixes CI, and addresses reviews autonomously.
|
||||||
fixes CI, and addresses reviews autonomously.
|
|
||||||
</p>
|
</p>
|
||||||
<div className="landing-fade-rise-d2 flex items-center gap-3 mt-10 flex-wrap justify-center">
|
<div className="landing-fade-rise-d2 flex items-center gap-3 mt-10 flex-wrap justify-center">
|
||||||
<div className="landing-card rounded-lg px-6 py-3 font-mono text-sm">
|
<div className="landing-card rounded-lg px-6 py-3 font-mono text-sm">
|
||||||
|
|
|
||||||
|
|
@ -78,12 +78,9 @@ export function LandingHowItWorks() {
|
||||||
return (
|
return (
|
||||||
<section className="py-[120px] px-6 max-w-[72rem] mx-auto" id="how">
|
<section className="py-[120px] px-6 max-w-[72rem] mx-auto" id="how">
|
||||||
<div className="landing-reveal">
|
<div className="landing-reveal">
|
||||||
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-60 mb-6">
|
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-60 mb-6">Process</div>
|
||||||
Process
|
|
||||||
</div>
|
|
||||||
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px] mb-6">
|
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px] mb-6">
|
||||||
Three steps to{" "}
|
Three steps to <em className="italic text-[var(--landing-muted)]">orchestration</em>
|
||||||
<em className="italic text-[var(--landing-muted)]">orchestration</em>
|
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -110,11 +107,7 @@ export function LandingHowItWorks() {
|
||||||
}}
|
}}
|
||||||
className="relative min-w-0 cursor-pointer overflow-hidden border-l border-[var(--landing-border-subtle)] pl-7 pr-5 py-2 first:border-l-0 first:pl-0 md:first:pl-7"
|
className="relative min-w-0 cursor-pointer overflow-hidden border-l border-[var(--landing-border-subtle)] pl-7 pr-5 py-2 first:border-l-0 first:pl-0 md:first:pl-7"
|
||||||
style={{
|
style={{
|
||||||
flex: isDesktop
|
flex: isDesktop ? (isActive ? "1 1 0%" : "0 1 15rem") : "0 0 auto",
|
||||||
? isActive
|
|
||||||
? "1 1 0%"
|
|
||||||
: "0 1 15rem"
|
|
||||||
: "0 0 auto",
|
|
||||||
transition: "flex 0.6s cubic-bezier(0.22,1,0.36,1)",
|
transition: "flex 0.6s cubic-bezier(0.22,1,0.36,1)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -132,17 +125,13 @@ export function LandingHowItWorks() {
|
||||||
<h3
|
<h3
|
||||||
className="font-sans font-[680] tracking-tight text-[1.375rem] leading-[1.15]"
|
className="font-sans font-[680] tracking-tight text-[1.375rem] leading-[1.15]"
|
||||||
style={{
|
style={{
|
||||||
color: isActive
|
color: isActive ? "var(--landing-fg)" : "var(--landing-muted)",
|
||||||
? "var(--landing-fg)"
|
|
||||||
: "var(--landing-muted)",
|
|
||||||
transition: "color 0.4s ease",
|
transition: "color 0.4s ease",
|
||||||
maxWidth: isActive ? "100%" : "11rem",
|
maxWidth: isActive ? "100%" : "11rem",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{step.title.replace(` ${step.titleEm}`, "")}{" "}
|
{step.title.replace(` ${step.titleEm}`, "")}{" "}
|
||||||
<em className="italic text-[var(--landing-muted)]">
|
<em className="italic text-[var(--landing-muted)]">{step.titleEm}</em>
|
||||||
{step.titleEm}
|
|
||||||
</em>
|
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* Expanding body */}
|
{/* Expanding body */}
|
||||||
|
|
@ -225,7 +214,9 @@ function CliDemo() {
|
||||||
<div className="text-[var(--landing-muted)] opacity-60"> </div>
|
<div className="text-[var(--landing-muted)] opacity-60"> </div>
|
||||||
<div>
|
<div>
|
||||||
<span className="landing-agent-dot mr-1.5" />
|
<span className="landing-agent-dot mr-1.5" />
|
||||||
<span className="text-[var(--landing-muted)] opacity-60">5 agents working · Dashboard → http://localhost:3000</span>
|
<span className="text-[var(--landing-muted)] opacity-60">
|
||||||
|
5 agents working · Dashboard → http://localhost:3000
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -242,19 +233,22 @@ function DashboardDemo() {
|
||||||
<span className="text-[0.6875rem] text-[var(--landing-muted)] opacity-50 ml-2">my-saas-app · 5 sessions</span>
|
<span className="text-[0.6875rem] text-[var(--landing-muted)] opacity-50 ml-2">my-saas-app · 5 sessions</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 gap-2 p-3">
|
<div className="grid grid-cols-4 gap-2 p-3">
|
||||||
<DashColumn title="Working" cards={[
|
<DashColumn
|
||||||
|
title="Working"
|
||||||
|
cards={[
|
||||||
{ title: "Add user auth flow", meta: "#42 · feat/auth", agent: "claude-code" },
|
{ title: "Add user auth flow", meta: "#42 · feat/auth", agent: "claude-code" },
|
||||||
{ title: "Fix pagination bug", meta: "#43 · fix/pagination", agent: "codex" },
|
{ title: "Fix pagination bug", meta: "#43 · fix/pagination", agent: "codex" },
|
||||||
]} />
|
]}
|
||||||
<DashColumn title="Pending" cards={[
|
/>
|
||||||
{ title: "Add rate limiting", meta: "#44 · PR #312", agent: "aider" },
|
<DashColumn title="Pending" cards={[{ title: "Add rate limiting", meta: "#44 · PR #312", agent: "aider" }]} />
|
||||||
]} />
|
<DashColumn
|
||||||
<DashColumn title="Review" cards={[
|
title="Review"
|
||||||
{ title: "Update API tests", meta: "#45 · PR #310", agent: "claude-code", amber: true },
|
cards={[{ title: "Update API tests", meta: "#45 · PR #310", agent: "claude-code", amber: true }]}
|
||||||
]} />
|
/>
|
||||||
<DashColumn title="Merged" cards={[
|
<DashColumn
|
||||||
{ title: "Refactor DB layer", meta: "#46 · PR #308", agent: "opencode", done: true },
|
title="Merged"
|
||||||
]} />
|
cards={[{ title: "Refactor DB layer", meta: "#46 · PR #308", agent: "opencode", done: true }]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -269,7 +263,10 @@ function PrsDemo() {
|
||||||
{ branch: "feat/rate-limiting", title: "Add Redis-backed rate limiter" },
|
{ branch: "feat/rate-limiting", title: "Add Redis-backed rate limiter" },
|
||||||
{ branch: "refactor/db-layer", title: "Extract repository pattern from services" },
|
{ branch: "refactor/db-layer", title: "Extract repository pattern from services" },
|
||||||
].map((pr) => (
|
].map((pr) => (
|
||||||
<div key={pr.branch} className="bg-[var(--landing-surface)] border border-[var(--landing-border-subtle)] rounded-xl px-5 py-4 flex items-center justify-between">
|
<div
|
||||||
|
key={pr.branch}
|
||||||
|
className="bg-[var(--landing-surface)] border border-[var(--landing-border-subtle)] rounded-xl px-5 py-4 flex items-center justify-between"
|
||||||
|
>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<div className="font-mono text-xs text-[var(--landing-fg)]/70">{pr.branch}</div>
|
<div className="font-mono text-xs text-[var(--landing-fg)]/70">{pr.branch}</div>
|
||||||
<div className="text-[0.8125rem] text-[var(--landing-muted)]">{pr.title}</div>
|
<div className="text-[0.8125rem] text-[var(--landing-muted)]">{pr.title}</div>
|
||||||
|
|
@ -298,14 +295,20 @@ function DashColumn({ title, cards }: { title: string; cards: DashCardData[] })
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
{cards.map((card) => (
|
{cards.map((card) => (
|
||||||
<div key={card.meta} className="bg-[var(--landing-surface)] border border-[var(--landing-border-subtle)] rounded-lg p-2.5 mb-1.5 text-[0.6875rem]">
|
<div
|
||||||
|
key={card.meta}
|
||||||
|
className="bg-[var(--landing-surface)] border border-[var(--landing-border-subtle)] rounded-lg p-2.5 mb-1.5 text-[0.6875rem]"
|
||||||
|
>
|
||||||
<div className="text-[var(--landing-fg)]/70 mb-1">{card.title}</div>
|
<div className="text-[var(--landing-fg)]/70 mb-1">{card.title}</div>
|
||||||
<div className="font-mono text-[0.5625rem] text-[var(--landing-muted)] opacity-50">{card.meta}</div>
|
<div className="font-mono text-[0.5625rem] text-[var(--landing-muted)] opacity-50">{card.meta}</div>
|
||||||
<div className="flex items-center gap-1 mt-1 font-mono text-[0.5625rem] text-[var(--landing-muted)] opacity-60">
|
<div className="flex items-center gap-1 mt-1 font-mono text-[0.5625rem] text-[var(--landing-muted)] opacity-60">
|
||||||
{card.done ? (
|
{card.done ? (
|
||||||
<span>✓</span>
|
<span>✓</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="inline-block w-1.5 h-1.5 rounded-full" style={{ background: card.amber ? "rgba(251,191,36,0.7)" : "rgba(134,239,172,0.7)" }} />
|
<span
|
||||||
|
className="inline-block w-1.5 h-1.5 rounded-full"
|
||||||
|
style={{ background: card.amber ? "rgba(251,191,36,0.7)" : "rgba(134,239,172,0.7)" }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{card.agent}
|
{card.agent}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -36,17 +36,26 @@ export function LandingNav() {
|
||||||
</a>
|
</a>
|
||||||
<ul className="hidden md:flex items-center gap-8 list-none">
|
<ul className="hidden md:flex items-center gap-8 list-none">
|
||||||
<li>
|
<li>
|
||||||
<a href="/docs" className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
|
<a
|
||||||
|
href="/docs"
|
||||||
|
className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors"
|
||||||
|
>
|
||||||
Docs
|
Docs
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#features" className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
|
<a
|
||||||
|
href="#features"
|
||||||
|
className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors"
|
||||||
|
>
|
||||||
Features
|
Features
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#how" className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
|
<a
|
||||||
|
href="#how"
|
||||||
|
className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors"
|
||||||
|
>
|
||||||
How It Works
|
How It Works
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
const steps = [
|
const steps = [
|
||||||
{ num: "STEP 01", title: "Install", desc: "One command. No dependencies beyond Node.js.", cmd: "npm i -g @aoagents/ao" },
|
{
|
||||||
{ num: "STEP 02", title: "Configure", desc: "Create an agent-orchestrator.yaml. Pick your agents, tracker, and notifiers.", cmd: "ao start" },
|
num: "STEP 01",
|
||||||
|
title: "Install",
|
||||||
|
desc: "One command. No dependencies beyond Node.js.",
|
||||||
|
cmd: "npm i -g @aoagents/ao",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: "STEP 02",
|
||||||
|
title: "Configure",
|
||||||
|
desc: "Create an agent-orchestrator.yaml. Pick your agents, tracker, and notifiers.",
|
||||||
|
cmd: "ao start",
|
||||||
|
},
|
||||||
{ num: "STEP 03", title: "Launch", desc: "Assign issues and watch agents spawn.", cmd: "ao batch-spawn 1 2 3" },
|
{ num: "STEP 03", title: "Launch", desc: "Assign issues and watch agents spawn.", cmd: "ao batch-spawn 1 2 3" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -12,8 +22,7 @@ export function LandingQuickStart() {
|
||||||
Get started in 60 seconds
|
Get started in 60 seconds
|
||||||
</div>
|
</div>
|
||||||
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px] mb-6">
|
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px] mb-6">
|
||||||
Three commands to{" "}
|
Three commands to <em className="italic text-[var(--landing-muted)]">launch</em>
|
||||||
<em className="italic text-[var(--landing-muted)]">launch</em>
|
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mt-12">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mt-12">
|
||||||
|
|
@ -22,12 +31,8 @@ export function LandingQuickStart() {
|
||||||
<div className="font-mono text-[0.625rem] tracking-[0.1em] text-[var(--landing-muted)] opacity-40 mb-3">
|
<div className="font-mono text-[0.625rem] tracking-[0.1em] text-[var(--landing-muted)] opacity-40 mb-3">
|
||||||
{s.num}
|
{s.num}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-sans font-[680] tracking-tight text-xl mb-2 tracking-tight">
|
<h3 className="font-sans font-[680] tracking-tight text-xl mb-2 tracking-tight">{s.title}</h3>
|
||||||
{s.title}
|
<p className="text-[var(--landing-muted)] text-[0.8125rem] leading-[1.6] mb-4">{s.desc}</p>
|
||||||
</h3>
|
|
||||||
<p className="text-[var(--landing-muted)] text-[0.8125rem] leading-[1.6] mb-4">
|
|
||||||
{s.desc}
|
|
||||||
</p>
|
|
||||||
<div className="font-mono text-xs text-[var(--landing-fg)]/70 bg-black/30 px-3.5 py-2.5 rounded-lg">
|
<div className="font-mono text-xs text-[var(--landing-fg)]/70 bg-black/30 px-3.5 py-2.5 rounded-lg">
|
||||||
<span className="text-[var(--landing-muted)] opacity-40">$</span> {s.cmd}
|
<span className="text-[var(--landing-muted)] opacity-40">$</span> {s.cmd}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -35,7 +40,10 @@ export function LandingQuickStart() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="landing-reveal mt-8 text-center">
|
<div className="landing-reveal mt-8 text-center">
|
||||||
<a href="/docs/" className="landing-card inline-flex rounded-lg px-4 py-2 text-[0.8125rem] text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
|
<a
|
||||||
|
href="/docs/"
|
||||||
|
className="landing-card inline-flex rounded-lg px-4 py-2 text-[0.8125rem] text-[var(--landing-muted)] no-underline hover:text-white transition-colors"
|
||||||
|
>
|
||||||
Explore docs for setup and workflows
|
Explore docs for setup and workflows
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,11 @@ export function LandingStats({ stats }: LandingStatsProps) {
|
||||||
<section className="py-20 px-6 max-w-[72rem] mx-auto">
|
<section className="py-20 px-6 max-w-[72rem] mx-auto">
|
||||||
<div className="landing-reveal grid grid-cols-2 md:grid-cols-4 gap-5">
|
<div className="landing-reveal grid grid-cols-2 md:grid-cols-4 gap-5">
|
||||||
{cards.map((stat) => (
|
{cards.map((stat) => (
|
||||||
<div
|
<div key={stat.label} className="landing-card rounded-2xl py-8 px-6 text-center">
|
||||||
key={stat.label}
|
|
||||||
className="landing-card rounded-2xl py-8 px-6 text-center"
|
|
||||||
>
|
|
||||||
<div className="font-sans font-[680] tracking-tight text-[clamp(2rem,4vw,3rem)] tracking-tight mb-1">
|
<div className="font-sans font-[680] tracking-tight text-[clamp(2rem,4vw,3rem)] tracking-tight mb-1">
|
||||||
{stat.number}
|
{stat.number}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-[var(--landing-muted)] opacity-60">
|
<div className="text-xs text-[var(--landing-muted)] opacity-60">{stat.label}</div>
|
||||||
{stat.label}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ import { useEffect, useState } from "react";
|
||||||
|
|
||||||
const testimonials = [
|
const testimonials = [
|
||||||
{
|
{
|
||||||
quote:
|
quote: "Set up 12 agents on our backlog before lunch. By end of day, 8 PRs were merged.",
|
||||||
"Set up 12 agents on our backlog before lunch. By end of day, 8 PRs were merged.",
|
|
||||||
img: "https://i.pravatar.cc/120?img=13",
|
img: "https://i.pravatar.cc/120?img=13",
|
||||||
name: "Staff Engineer",
|
name: "Staff Engineer",
|
||||||
role: "Series B Startup",
|
role: "Series B Startup",
|
||||||
|
|
@ -44,10 +43,7 @@ export function LandingTestimonials() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (paused) return;
|
if (paused) return;
|
||||||
const t = window.setTimeout(
|
const t = window.setTimeout(() => change((active + 1) % testimonials.length), ROTATE_MS);
|
||||||
() => change((active + 1) % testimonials.length),
|
|
||||||
ROTATE_MS,
|
|
||||||
);
|
|
||||||
return () => window.clearTimeout(t);
|
return () => window.clearTimeout(t);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [active, paused]);
|
}, [active, paused]);
|
||||||
|
|
@ -65,11 +61,7 @@ export function LandingTestimonials() {
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div className="landing-reveal mt-16" onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}>
|
||||||
className="landing-reveal mt-16"
|
|
||||||
onMouseEnter={() => setPaused(true)}
|
|
||||||
onMouseLeave={() => setPaused(false)}
|
|
||||||
>
|
|
||||||
{/* Quote — fades on change */}
|
{/* Quote — fades on change */}
|
||||||
<div className="min-h-[8rem] max-w-[58rem]">
|
<div className="min-h-[8rem] max-w-[58rem]">
|
||||||
<blockquote
|
<blockquote
|
||||||
|
|
@ -128,10 +120,7 @@ export function LandingTestimonials() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Vertical divider */}
|
{/* Vertical divider */}
|
||||||
<div
|
<div className="w-px h-10 shrink-0" style={{ background: "var(--landing-border-default)" }} />
|
||||||
className="w-px h-10 shrink-0"
|
|
||||||
style={{ background: "var(--landing-border-default)" }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Author — fades on change */}
|
{/* Author — fades on change */}
|
||||||
<div
|
<div
|
||||||
|
|
@ -140,12 +129,8 @@ export function LandingTestimonials() {
|
||||||
transition: "opacity 0.4s ease 0.05s",
|
transition: "opacity 0.4s ease 0.05s",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-[0.9375rem] font-medium text-[var(--landing-fg)]">
|
<div className="text-[0.9375rem] font-medium text-[var(--landing-fg)]">{t.name}</div>
|
||||||
{t.name}
|
<div className="text-[0.8125rem] text-[var(--landing-muted)] opacity-60">{t.role}</div>
|
||||||
</div>
|
|
||||||
<div className="text-[0.8125rem] text-[var(--landing-muted)] opacity-60">
|
|
||||||
{t.role}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,7 @@ export function LandingUseCases() {
|
||||||
One orchestrator, many jobs
|
One orchestrator, many jobs
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.6] max-w-[34rem] mx-auto mb-12">
|
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.6] max-w-[34rem] mx-auto mb-12">
|
||||||
Point AO at the work and walk away — drag to explore what a single
|
Point AO at the work and walk away — drag to explore what a single run can do.
|
||||||
run can do.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -157,10 +156,8 @@ export function LandingUseCases() {
|
||||||
maxWidth: "1120px",
|
maxWidth: "1120px",
|
||||||
cursor: "grab",
|
cursor: "grab",
|
||||||
touchAction: "pan-y",
|
touchAction: "pan-y",
|
||||||
WebkitMaskImage:
|
WebkitMaskImage: "linear-gradient(to right, transparent, #000 16%, #000 84%, transparent)",
|
||||||
"linear-gradient(to right, transparent, #000 16%, #000 84%, transparent)",
|
maskImage: "linear-gradient(to right, transparent, #000 16%, #000 84%, transparent)",
|
||||||
maskImage:
|
|
||||||
"linear-gradient(to right, transparent, #000 16%, #000 84%, transparent)",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -201,10 +198,7 @@ export function LandingUseCases() {
|
||||||
<div className="font-mono text-[0.6875rem] tracking-[0.12em] uppercase text-[var(--landing-accent)] opacity-80">
|
<div className="font-mono text-[0.6875rem] tracking-[0.12em] uppercase text-[var(--landing-accent)] opacity-80">
|
||||||
{c.eyebrow}
|
{c.eyebrow}
|
||||||
</div>
|
</div>
|
||||||
<h3
|
<h3 className="font-sans font-[680] text-[1.3125rem] tracking-tight" style={{ marginTop: "1rem" }}>
|
||||||
className="font-sans font-[680] text-[1.3125rem] tracking-tight"
|
|
||||||
style={{ marginTop: "1rem" }}
|
|
||||||
>
|
|
||||||
{c.title}
|
{c.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p
|
<p
|
||||||
|
|
@ -218,12 +212,9 @@ export function LandingUseCases() {
|
||||||
style={{ marginTop: "auto", padding: "0.75rem 0.875rem" }}
|
style={{ marginTop: "auto", padding: "0.75rem 0.875rem" }}
|
||||||
>
|
>
|
||||||
<div className="whitespace-nowrap overflow-hidden text-ellipsis">
|
<div className="whitespace-nowrap overflow-hidden text-ellipsis">
|
||||||
<span className={dim}>{c.prefix}</span>{" "}
|
<span className={dim}>{c.prefix}</span> <span className={fg}>{c.cmd}</span>
|
||||||
<span className={fg}>{c.cmd}</span>
|
|
||||||
</div>
|
|
||||||
<div className={`whitespace-nowrap overflow-hidden text-ellipsis ${ok}`}>
|
|
||||||
→ {c.outcome}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className={`whitespace-nowrap overflow-hidden text-ellipsis ${ok}`}>→ {c.outcome}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,42 @@ type Milestone = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const milestones: Milestone[] = [
|
const milestones: Milestone[] = [
|
||||||
{ key: "spawning", label: "Spawn", icon: "spawn", desc: "Each issue spawns an agent in its own git worktree — isolated branch, isolated context." },
|
{
|
||||||
{ key: "working", label: "Work", icon: "work", desc: "The agent writes code, runs the test suite, and commits. Watch it live or let it run." },
|
key: "spawning",
|
||||||
{ key: "pr_open", label: "Open PR", icon: "pr", desc: "Work is pushed and a pull request opens against main with a summary of the changes." },
|
label: "Spawn",
|
||||||
{ key: "review", label: "CI & review", icon: "review", desc: "CI fails? It reads the logs and pushes a fix. Review comments land? It addresses them." },
|
icon: "spawn",
|
||||||
{ key: "mergeable", label: "Mergeable", icon: "mergeable", desc: "Green checks, approvals in. The PR settles into a clean, mergeable state." },
|
desc: "Each issue spawns an agent in its own git worktree — isolated branch, isolated context.",
|
||||||
{ key: "merged", label: "Merged", icon: "merged", desc: "It lands on main, the worktree is archived, and the session is marked done." },
|
},
|
||||||
|
{
|
||||||
|
key: "working",
|
||||||
|
label: "Work",
|
||||||
|
icon: "work",
|
||||||
|
desc: "The agent writes code, runs the test suite, and commits. Watch it live or let it run.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "pr_open",
|
||||||
|
label: "Open PR",
|
||||||
|
icon: "pr",
|
||||||
|
desc: "Work is pushed and a pull request opens against main with a summary of the changes.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "review",
|
||||||
|
label: "CI & review",
|
||||||
|
icon: "review",
|
||||||
|
desc: "CI fails? It reads the logs and pushes a fix. Review comments land? It addresses them.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "mergeable",
|
||||||
|
label: "Mergeable",
|
||||||
|
icon: "mergeable",
|
||||||
|
desc: "Green checks, approvals in. The PR settles into a clean, mergeable state.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "merged",
|
||||||
|
label: "Merged",
|
||||||
|
icon: "merged",
|
||||||
|
desc: "It lands on main, the worktree is archived, and the session is marked done.",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Ruler geometry (viewBox units)
|
// Ruler geometry (viewBox units)
|
||||||
|
|
@ -58,10 +88,7 @@ export function LandingWorkflow() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = wrapRef.current;
|
const el = wrapRef.current;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
const ob = new IntersectionObserver(
|
const ob = new IntersectionObserver(([entry]) => entry.isIntersecting && setInView(true), { threshold: 0.25 });
|
||||||
([entry]) => entry.isIntersecting && setInView(true),
|
|
||||||
{ threshold: 0.25 },
|
|
||||||
);
|
|
||||||
ob.observe(el);
|
ob.observe(el);
|
||||||
return () => ob.disconnect();
|
return () => ob.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -96,10 +123,7 @@ export function LandingWorkflow() {
|
||||||
// Auto-loop
|
// Auto-loop
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!inView || paused) return;
|
if (!inView || paused) return;
|
||||||
const t = window.setTimeout(
|
const t = window.setTimeout(() => setActive((a) => (a + 1) % milestones.length), STEP_MS);
|
||||||
() => setActive((a) => (a + 1) % milestones.length),
|
|
||||||
STEP_MS,
|
|
||||||
);
|
|
||||||
return () => window.clearTimeout(t);
|
return () => window.clearTimeout(t);
|
||||||
}, [active, paused, inView]);
|
}, [active, paused, inView]);
|
||||||
|
|
||||||
|
|
@ -189,9 +213,7 @@ export function LandingWorkflow() {
|
||||||
style={{ opacity: show ? 1 : 0, transition: "opacity 0.35s ease" }}
|
style={{ opacity: show ? 1 : 0, transition: "opacity 0.35s ease" }}
|
||||||
>
|
>
|
||||||
<LifecycleIcon kind={cur.icon} />
|
<LifecycleIcon kind={cur.icon} />
|
||||||
<div className="font-sans font-[680] tracking-tight text-[1.5rem] tracking-[-0.5px] mt-3">
|
<div className="font-sans font-[680] tracking-tight text-[1.5rem] tracking-[-0.5px] mt-3">{cur.label}</div>
|
||||||
{cur.label}
|
|
||||||
</div>
|
|
||||||
<div className="font-mono text-[0.6875rem] tracking-[0.08em] text-[var(--landing-accent)] opacity-80 mt-1">
|
<div className="font-mono text-[0.6875rem] tracking-[0.08em] text-[var(--landing-accent)] opacity-80 mt-1">
|
||||||
{cur.key}
|
{cur.key}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -208,7 +230,15 @@ export function LandingWorkflow() {
|
||||||
>
|
>
|
||||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-auto" role="img" aria-label="Lifecycle scrubber">
|
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-auto" role="img" aria-label="Lifecycle scrubber">
|
||||||
{/* center indicator */}
|
{/* center indicator */}
|
||||||
<line x1={CENTER_X} y1={4} x2={CENTER_X} y2={H - 8} stroke="var(--landing-accent)" strokeWidth={2} strokeLinecap="round" />
|
<line
|
||||||
|
x1={CENTER_X}
|
||||||
|
y1={4}
|
||||||
|
x2={CENTER_X}
|
||||||
|
y2={H - 8}
|
||||||
|
stroke="var(--landing-accent)"
|
||||||
|
strokeWidth={2}
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
<circle cx={CENTER_X} cy={arcTop(0)} r={4} fill="var(--landing-accent)" />
|
<circle cx={CENTER_X} cy={arcTop(0)} r={4} fill="var(--landing-accent)" />
|
||||||
|
|
||||||
{/* ticks */}
|
{/* ticks */}
|
||||||
|
|
@ -257,7 +287,6 @@ export function LandingWorkflow() {
|
||||||
>
|
>
|
||||||
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.6]">{cur.desc}</p>
|
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.6]">{cur.desc}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -145,11 +145,6 @@ export function PageConstellation() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<canvas
|
<canvas ref={canvasRef} className="fixed inset-0 pointer-events-none" style={{ zIndex: 0 }} aria-hidden="true" />
|
||||||
ref={canvasRef}
|
|
||||||
className="fixed inset-0 pointer-events-none"
|
|
||||||
style={{ zIndex: 0 }}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export function ScrollRevealProvider({ children }: { children: React.ReactNode }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ threshold: 0.1, rootMargin: "-50px" }
|
{ threshold: 0.1, rootMargin: "-50px" },
|
||||||
);
|
);
|
||||||
|
|
||||||
document.querySelectorAll(".landing-reveal").forEach((el) => {
|
document.querySelectorAll(".landing-reveal").forEach((el) => {
|
||||||
|
|
|
||||||
|
|
@ -19,29 +19,19 @@ export function DocsMissingPage() {
|
||||||
<DocsBody>
|
<DocsBody>
|
||||||
<div className="not-prose docs-missing-wrap">
|
<div className="not-prose docs-missing-wrap">
|
||||||
<div className="docs-missing-card">
|
<div className="docs-missing-card">
|
||||||
<div className="docs-missing-label">
|
<div className="docs-missing-label">docs / checkout failed</div>
|
||||||
docs / checkout failed
|
|
||||||
</div>
|
|
||||||
<div className="docs-missing-content">
|
<div className="docs-missing-content">
|
||||||
<section className="docs-missing-copy">
|
<section className="docs-missing-copy">
|
||||||
<h2>
|
<h2>This page checked out the wrong worktree.</h2>
|
||||||
This page checked out the wrong worktree.
|
|
||||||
</h2>
|
|
||||||
<p>
|
<p>
|
||||||
The docs were rebuilt, and this URL did not survive the merge. Start from the docs
|
The docs were rebuilt, and this URL did not survive the merge. Start from the docs index, or use
|
||||||
index, or use search in the sidebar to find where it landed.
|
search in the sidebar to find where it landed.
|
||||||
</p>
|
</p>
|
||||||
<div className="docs-missing-actions">
|
<div className="docs-missing-actions">
|
||||||
<Link
|
<Link href="/docs" className="docs-missing-primary">
|
||||||
href="/docs"
|
|
||||||
className="docs-missing-primary"
|
|
||||||
>
|
|
||||||
Browse docs
|
Browse docs
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link href="/" className="docs-missing-secondary">
|
||||||
href="/"
|
|
||||||
className="docs-missing-secondary"
|
|
||||||
>
|
|
||||||
Home
|
Home
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,7 @@ function Cell({ platform, status }: { platform: "macos" | "linux" | "windows"; s
|
||||||
>
|
>
|
||||||
<Logo name={logoName} size={18} />
|
<Logo name={logoName} size={18} />
|
||||||
<div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
|
<div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
|
||||||
<span style={{ fontSize: "0.8125rem", fontWeight: 600, color: "var(--color-fd-foreground)" }}>
|
<span style={{ fontSize: "0.8125rem", fontWeight: 600, color: "var(--color-fd-foreground)" }}>{title}</span>
|
||||||
{title}
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: "0.75rem",
|
fontSize: "0.75rem",
|
||||||
|
|
@ -73,12 +71,7 @@ function Cell({ platform, status }: { platform: "macos" | "linux" | "windows"; s
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PlatformSupport({
|
export function PlatformSupport({ macos = "full", linux = "full", windows = "full", note }: PlatformSupportProps) {
|
||||||
macos = "full",
|
|
||||||
linux = "full",
|
|
||||||
windows = "full",
|
|
||||||
note,
|
|
||||||
}: PlatformSupportProps) {
|
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: "1.25rem 0" }}>
|
<div style={{ margin: "1.25rem 0" }}>
|
||||||
<div style={{ display: "flex", gap: "0.5rem", flexWrap: "wrap" }}>
|
<div style={{ display: "flex", gap: "0.5rem", flexWrap: "wrap" }}>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ Agent Orchestrator (AO) is a Node.js orchestrator that spawns and manages parall
|
||||||
Each abstraction in AO is a named interface defined in `packages/core/src/types.ts`. Seven of the eight slots are pluggable at runtime; the eighth (Lifecycle) is built into core and cannot be replaced.
|
Each abstraction in AO is a named interface defined in `packages/core/src/types.ts`. Seven of the eight slots are pluggable at runtime; the eighth (Lifecycle) is built into core and cannot be replaced.
|
||||||
|
|
||||||
| Slot | Default | Purpose | Interface |
|
| Slot | Default | Purpose | Interface |
|
||||||
|------|---------|---------|-----------|
|
| --------- | ----------------------------------------------- | ---------------------------------------------------------------- | ------------------ |
|
||||||
| Runtime | [tmux](/docs/plugins/runtimes/tmux) | Where agent sessions execute (tmux, process, docker, k8s) | `Runtime` |
|
| Runtime | [tmux](/docs/plugins/runtimes/tmux) | Where agent sessions execute (tmux, process, docker, k8s) | `Runtime` |
|
||||||
| Agent | [claude-code](/docs/plugins/agents/claude-code) | Which AI coding tool is launched | `Agent` |
|
| Agent | [claude-code](/docs/plugins/agents/claude-code) | Which AI coding tool is launched | `Agent` |
|
||||||
| Workspace | [worktree](/docs/plugins/workspaces/worktree) | Code isolation — each session gets its own git worktree or clone | `Workspace` |
|
| Workspace | [worktree](/docs/plugins/workspaces/worktree) | Code isolation — each session gets its own git worktree or clone | `Workspace` |
|
||||||
|
|
@ -21,7 +21,9 @@ Each abstraction in AO is a named interface defined in `packages/core/src/types.
|
||||||
| Lifecycle | core (non-pluggable) | State machine, poll loop, and reaction engine | `LifecycleManager` |
|
| Lifecycle | core (non-pluggable) | State machine, poll loop, and reaction engine | `LifecycleManager` |
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
The Lifecycle slot is not pluggable. It is instantiated by core and wired to all other plugins automatically. You configure its behaviour (poll interval, reactions, thresholds) through `agent-orchestrator.yaml` rather than by replacing the implementation.
|
The Lifecycle slot is not pluggable. It is instantiated by core and wired to all other plugins automatically. You
|
||||||
|
configure its behaviour (poll interval, reactions, thresholds) through `agent-orchestrator.yaml` rather than by
|
||||||
|
replacing the implementation.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -57,7 +59,7 @@ pr_open ────────────────────────
|
||||||
Terminal statuses (session is dead and will no longer be polled): `killed`, `terminated`, `done`, `cleanup`, `errored`, `merged`.
|
Terminal statuses (session is dead and will no longer be polled): `killed`, `terminated`, `done`, `cleanup`, `errored`, `merged`.
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------------------- | ----------------------------------------------------------------------------- |
|
||||||
| `spawning` | Session is being created — worktree, branch, and tmux window are initialising |
|
| `spawning` | Session is being created — worktree, branch, and tmux window are initialising |
|
||||||
| `working` | Agent is active; no PR yet |
|
| `working` | Agent is active; no PR yet |
|
||||||
| `pr_open` | Agent has pushed a PR; CI and reviews are pending |
|
| `pr_open` | Agent has pushed a PR; CI and reviews are pending |
|
||||||
|
|
@ -100,7 +102,7 @@ Priority is inferred by `inferPriority()` in `lifecycle-manager.ts`:
|
||||||
- **info** — everything else, including all `summary.*` events
|
- **info** — everything else, including all `summary.*` events
|
||||||
|
|
||||||
| `event.type` | Priority | When emitted |
|
| `event.type` | Priority | When emitted |
|
||||||
|---|---|---|
|
| ---------------------------- | -------- | -------------------------------------------------- |
|
||||||
| `session.spawned` | info | Session transitions out of `spawning` |
|
| `session.spawned` | info | Session transitions out of `spawning` |
|
||||||
| `session.working` | info | Session enters `working` |
|
| `session.working` | info | Session enters `working` |
|
||||||
| `session.exited` | info | Agent process exits |
|
| `session.exited` | info | Agent process exits |
|
||||||
|
|
@ -205,7 +207,7 @@ Every agent plugin must implement `getActivityState(session, readyThresholdMs?)`
|
||||||
### The 6 activity states
|
### The 6 activity states
|
||||||
|
|
||||||
| State | Meaning | When |
|
| State | Meaning | When |
|
||||||
|---|---|---|
|
| --------------- | ----------------------------------------------------------- | ----------------------------------------------------------- |
|
||||||
| `active` | Agent is processing — thinking, writing code, running tools | Activity within the last 30 seconds |
|
| `active` | Agent is processing — thinking, writing code, running tools | Activity within the last 30 seconds |
|
||||||
| `ready` | Agent finished its turn and is alive, waiting for input | 30 seconds – 5 minutes since last activity |
|
| `ready` | Agent finished its turn and is alive, waiting for input | 30 seconds – 5 minutes since last activity |
|
||||||
| `idle` | Agent has been quiet for an extended period | More than 5 minutes since last activity (default threshold) |
|
| `idle` | Agent has been quiet for an extended period | More than 5 minutes since last activity (default threshold) |
|
||||||
|
|
@ -237,20 +239,22 @@ Every agent plugin must implement this cascade in order:
|
||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="warn">
|
<Callout type="warn">
|
||||||
Step 4 (the JSONL entry fallback) is mandatory. Skipping it means `getActivityState` returns `null` whenever the native API fails — the dashboard shows no activity state and stuck-detection breaks for the entire session lifetime. This was a real bug in the OpenCode plugin.
|
Step 4 (the JSONL entry fallback) is mandatory. Skipping it means `getActivityState` returns `null` whenever the
|
||||||
|
native API fails — the dashboard shows no activity state and stuck-detection breaks for the entire session lifetime.
|
||||||
|
This was a real bug in the OpenCode plugin.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
### Two JSONL patterns
|
### Two JSONL patterns
|
||||||
|
|
||||||
| Pattern | Used by | How it works |
|
| Pattern | Used by | How it works |
|
||||||
|---|---|---|
|
| ---------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **Agent-native JSONL** | Claude Code, Codex | The agent writes its own JSONL with rich state entries (`permission_request`, `tool_call`, `error`, etc.). `getActivityState` reads the last entry and maps it to activity states. |
|
| **Agent-native JSONL** | Claude Code, Codex | The agent writes its own JSONL with rich state entries (`permission_request`, `tool_call`, `error`, etc.). `getActivityState` reads the last entry and maps it to activity states. |
|
||||||
| **AO activity JSONL** | Aider, OpenCode, new agents | The agent implements `recordActivity`, which calls `recordTerminalActivity()` → `classifyTerminalActivity()` → `appendActivityEntry()` to write to `{workspacePath}/.ao/activity.jsonl`. `getActivityState` reads from this file. |
|
| **AO activity JSONL** | Aider, OpenCode, new agents | The agent implements `recordActivity`, which calls `recordTerminalActivity()` → `classifyTerminalActivity()` → `appendActivityEntry()` to write to `{workspacePath}/.ao/activity.jsonl`. `getActivityState` reads from this file. |
|
||||||
|
|
||||||
### Thresholds
|
### Thresholds
|
||||||
|
|
||||||
| Constant | Value | Purpose |
|
| Constant | Value | Purpose |
|
||||||
|---|---|---|
|
| ----------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `DEFAULT_ACTIVE_WINDOW_MS` | 30 seconds | Activity newer than this is `active`; older is `ready` |
|
| `DEFAULT_ACTIVE_WINDOW_MS` | 30 seconds | Activity newer than this is `active`; older is `ready` |
|
||||||
| `DEFAULT_READY_THRESHOLD_MS` | 5 minutes | `ready` sessions older than this become `idle` |
|
| `DEFAULT_READY_THRESHOLD_MS` | 5 minutes | `ready` sessions older than this become `idle` |
|
||||||
| `ACTIVITY_INPUT_STALENESS_MS` | 5 minutes | Deprecated compatibility export. `waiting_input` / `blocked` entries no longer expire by wallclock; they persist until process death or a newer entry overrides them. |
|
| `ACTIVITY_INPUT_STALENESS_MS` | 5 minutes | Deprecated compatibility export. `waiting_input` / `blocked` entries no longer expire by wallclock; they persist until process death or a newer entry overrides them. |
|
||||||
|
|
@ -270,6 +274,7 @@ Claude Code writes `.claude/settings.json` with a `PostToolUse` hook that fires
|
||||||
Agents without a native hook system (Codex, Aider, OpenCode, custom agents) use `~/.ao/bin/gh` and `~/.ao/bin/git` shell wrappers. These wrappers are installed to `~/.ao/bin/` by `setupPathWrapperWorkspace(workspacePath)` from `packages/core/src/agent-workspace-hooks.ts`. The function also writes session context to `{workspacePath}/.ao/AGENTS.md` (gitignored — does not touch tracked files).
|
Agents without a native hook system (Codex, Aider, OpenCode, custom agents) use `~/.ao/bin/gh` and `~/.ao/bin/git` shell wrappers. These wrappers are installed to `~/.ao/bin/` by `setupPathWrapperWorkspace(workspacePath)` from `packages/core/src/agent-workspace-hooks.ts`. The function also writes session context to `{workspacePath}/.ao/AGENTS.md` (gitignored — does not touch tracked files).
|
||||||
|
|
||||||
The wrappers intercept:
|
The wrappers intercept:
|
||||||
|
|
||||||
- `gh pr create` — captures the PR URL from stdout and writes `pr=<url>` and `status=pr_open`
|
- `gh pr create` — captures the PR URL from stdout and writes `pr=<url>` and `status=pr_open`
|
||||||
- `gh pr merge` — writes `status=merged`
|
- `gh pr merge` — writes `status=merged`
|
||||||
- `git checkout -b <branch>` / `git switch -c <branch>` — writes `branch=<name>`
|
- `git checkout -b <branch>` / `git switch -c <branch>` — writes `branch=<name>`
|
||||||
|
|
@ -333,8 +338,24 @@ agent-orchestrator.yaml ──► Config Loader (Zod) ──► Plugin Registry
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Project Configuration" href="/docs/configuration/projects" description="Configure projects, agents, trackers, and reactions in agent-orchestrator.yaml." />
|
<Card
|
||||||
<Card title="Authoring Plugins" href="/docs/plugins/authoring" description="Build custom runtime, agent, tracker, SCM, notifier, or terminal plugins." />
|
title="Project Configuration"
|
||||||
<Card title="Reactions" href="/docs/configuration/reactions" description="Set up automated reactions to CI failures, review comments, and merge events." />
|
href="/docs/configuration/projects"
|
||||||
<Card title="Configuration" href="/docs/configuration" description="Understand the global registry, local project config, and where AO stores runtime data." />
|
description="Configure projects, agents, trackers, and reactions in agent-orchestrator.yaml."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Authoring Plugins"
|
||||||
|
href="/docs/plugins/authoring"
|
||||||
|
description="Build custom runtime, agent, tracker, SCM, notifier, or terminal plugins."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Reactions"
|
||||||
|
href="/docs/configuration/reactions"
|
||||||
|
description="Set up automated reactions to CI failures, review comments, and merge events."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Configuration"
|
||||||
|
href="/docs/configuration"
|
||||||
|
description="Understand the global registry, local project config, and where AO stores runtime data."
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ ao
|
||||||
> Start orchestrator agent and dashboard (auto-creates config on first run, adds projects by path/URL)
|
> Start orchestrator agent and dashboard (auto-creates config on first run, adds projects by path/URL)
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ------------------- | ------- | ------------------------------------------------------------------------------- |
|
||||||
| `--no-dashboard` | — | Skip starting the dashboard server |
|
| `--no-dashboard` | — | Skip starting the dashboard server |
|
||||||
| `--no-orchestrator` | — | Skip starting the orchestrator agent |
|
| `--no-orchestrator` | — | Skip starting the orchestrator agent |
|
||||||
| `--rebuild` | — | Clean and rebuild the dashboard before starting |
|
| `--rebuild` | — | Clean and rebuild the dashboard before starting |
|
||||||
|
|
@ -54,7 +54,7 @@ If AO is already running, you'll get an interactive prompt to open/restart/spawn
|
||||||
> Stop orchestrator agent and dashboard
|
> Stop orchestrator agent and dashboard
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ----------------- | ------- | ------------------------------------------------ |
|
||||||
| `--purge-session` | — | Delete the mapped OpenCode session when stopping |
|
| `--purge-session` | — | Delete the mapped OpenCode session when stopping |
|
||||||
| `--all` | — | Stop all running AO instances on this machine |
|
| `--all` | — | Stop all running AO instances on this machine |
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ If AO is already running, you'll get an interactive prompt to open/restart/spawn
|
||||||
> Show all sessions with branch, activity, PR, and CI status
|
> Show all sessions with branch, activity, PR, and CI status
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ---------------------- | ------- | --------------------- |
|
||||||
| `-p, --project <id>` | — | Filter to one project |
|
| `-p, --project <id>` | — | Filter to one project |
|
||||||
| `--json` | — | Output JSON |
|
| `--json` | — | Output JSON |
|
||||||
| `-w, --watch` | — | Refresh continuously |
|
| `-w, --watch` | — | Refresh continuously |
|
||||||
|
|
@ -76,7 +76,7 @@ If AO is already running, you'll get an interactive prompt to open/restart/spawn
|
||||||
> Spawn a single agent session
|
> Spawn a single agent session
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| -------------------- | -------------- | --------------------------------------------------------------------------------- |
|
||||||
| `--open` | — | Open the session in a terminal tab |
|
| `--open` | — | Open the session in a terminal tab |
|
||||||
| `--agent <name>` | config default | Override the agent plugin (`claude-code`, `codex`, `cursor`, `aider`, `opencode`) |
|
| `--agent <name>` | config default | Override the agent plugin (`claude-code`, `codex`, `cursor`, `aider`, `opencode`) |
|
||||||
| `--claim-pr <pr>` | — | Immediately claim an existing PR for the spawned session |
|
| `--claim-pr <pr>` | — | Immediately claim an existing PR for the spawned session |
|
||||||
|
|
@ -86,7 +86,8 @@ If AO is already running, you'll get an interactive prompt to open/restart/spawn
|
||||||
Positional `[first]` — issue identifier. The project is auto-detected from `cwd`.
|
Positional `[first]` — issue identifier. The project is auto-detected from `cwd`.
|
||||||
|
|
||||||
<Callout type="warn">
|
<Callout type="warn">
|
||||||
The old two-arg form `ao spawn <project> <issue>` is rejected with an error. Use `-p` or run from inside a worktree.
|
The old two-arg form `ao spawn <project> <issue>` is rejected with an error. Use `-p` or run from inside a
|
||||||
|
worktree.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
### `ao batch-spawn <issues...>`
|
### `ao batch-spawn <issues...>`
|
||||||
|
|
@ -94,7 +95,7 @@ Positional `[first]` — issue identifier. The project is auto-detected from `cw
|
||||||
> Spawn sessions for multiple issues with duplicate detection
|
> Spawn sessions for multiple issues with duplicate detection
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| -------- | ------- | ------------------------------ |
|
||||||
| `--open` | — | Open sessions in terminal tabs |
|
| `--open` | — | Open sessions in terminal tabs |
|
||||||
|
|
||||||
Skips duplicates within the batch and any issues that already have an active session.
|
Skips duplicates within the batch and any issues that already have an active session.
|
||||||
|
|
@ -104,7 +105,7 @@ Skips duplicates within the batch and any issues that already have an active ses
|
||||||
> Send a message to a session with busy detection and retry
|
> Send a message to a session with busy detection and retry
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| --------------------- | ------- | -------------------------------------------------------- |
|
||||||
| `-f, --file <path>` | — | Send contents of a file instead of an inline message |
|
| `-f, --file <path>` | — | Send contents of a file instead of an inline message |
|
||||||
| `--no-wait` | — | Don't wait for the session to become idle before sending |
|
| `--no-wait` | — | Don't wait for the session to become idle before sending |
|
||||||
| `--timeout <seconds>` | `600` | Max seconds to wait for idle |
|
| `--timeout <seconds>` | `600` | Max seconds to wait for idle |
|
||||||
|
|
@ -116,7 +117,7 @@ Positional: `<session>` required, `[message...]` variadic. One of inline message
|
||||||
> Check PRs for review comments and trigger agents to address them
|
> Check PRs for review comments and trigger agents to address them
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ----------- | ------- | ------------------------------------------ |
|
||||||
| `--dry-run` | — | Show what would happen; don't nudge agents |
|
| `--dry-run` | — | Show what would happen; don't nudge agents |
|
||||||
|
|
||||||
Checks all projects if `[project]` is omitted.
|
Checks all projects if `[project]` is omitted.
|
||||||
|
|
@ -126,7 +127,7 @@ Checks all projects if `[project]` is omitted.
|
||||||
> Start the web dashboard
|
> Start the web dashboard
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ------------------- | ----------------------- | ----------------------------------------- |
|
||||||
| `-p, --port <port>` | config `port` or `3000` | Port to listen on |
|
| `-p, --port <port>` | config `port` or `3000` | Port to listen on |
|
||||||
| `--no-open` | — | Don't open the browser |
|
| `--no-open` | — | Don't open the browser |
|
||||||
| `--rebuild` | — | Clean stale Next.js artifacts and rebuild |
|
| `--rebuild` | — | Clean stale Next.js artifacts and rebuild |
|
||||||
|
|
@ -138,7 +139,7 @@ If the build looks stale you'll see a suggestion to run `ao dashboard --rebuild`
|
||||||
> Open session(s) in terminal tabs
|
> Open session(s) in terminal tabs
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ------------------ | ------- | ---------------------------------------------- |
|
||||||
| `-w, --new-window` | — | Open in a new terminal window instead of a tab |
|
| `-w, --new-window` | — | Open in a new terminal window instead of a tab |
|
||||||
|
|
||||||
Positional `[target]` — session name, project id, or `"all"`. Defaults to all sessions. Falls back to printing the dashboard URL when the native terminal helper isn't available (anywhere but macOS + iTerm2).
|
Positional `[target]` — session name, project id, or `"all"`. Defaults to all sessions. Falls back to printing the dashboard URL when the native terminal helper isn't available (anywhere but macOS + iTerm2).
|
||||||
|
|
@ -148,7 +149,7 @@ Positional `[target]` — session name, project id, or `"all"`. Defaults to all
|
||||||
> Mark an issue as verified (or failed) after checking the fix on staging
|
> Mark an issue as verified (or failed) after checking the fix on staging
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| --------------------- | ------- | ------------------------------------------------ |
|
||||||
| `-p, --project <id>` | — | Required if multiple projects |
|
| `-p, --project <id>` | — | Required if multiple projects |
|
||||||
| `--fail` | — | Mark as failed instead of passing |
|
| `--fail` | — | Mark as failed instead of passing |
|
||||||
| `-c, --comment <msg>` | — | Custom comment to add |
|
| `-c, --comment <msg>` | — | Custom comment to add |
|
||||||
|
|
@ -161,7 +162,7 @@ Positional `[target]` — session name, project id, or `"all"`. Defaults to all
|
||||||
> Run install, environment, and runtime health checks
|
> Run install, environment, and runtime health checks
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| --------------- | ------- | --------------------------------------------------------- |
|
||||||
| `--fix` | — | Apply safe fixes for launcher and stale-temp issues |
|
| `--fix` | — | Apply safe fixes for launcher and stale-temp issues |
|
||||||
| `--test-notify` | — | Send a test notification through each configured notifier |
|
| `--test-notify` | — | Send a test notification through each configured notifier |
|
||||||
|
|
||||||
|
|
@ -172,7 +173,7 @@ Runs `ao-doctor.sh` for shell-level checks, then TypeScript checks (version fres
|
||||||
> Check for updates and upgrade AO to the latest version
|
> Check for updates and upgrade AO to the latest version
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| -------------- | ------- | ------------------------------------------------------------------ |
|
||||||
| `--skip-smoke` | — | Skip smoke tests after rebuilding (git installs only) |
|
| `--skip-smoke` | — | Skip smoke tests after rebuilding (git installs only) |
|
||||||
| `--smoke-only` | — | Run smoke tests without fetching or rebuilding (git installs only) |
|
| `--smoke-only` | — | Run smoke tests without fetching or rebuilding (git installs only) |
|
||||||
| `--check` | — | Print version info as JSON; don't upgrade |
|
| `--check` | — | Print version info as JSON; don't upgrade |
|
||||||
|
|
@ -192,7 +193,7 @@ No flags.
|
||||||
> Send a manual demo notification without spawning sessions
|
> Send a manual demo notification without spawning sessions
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| -------------------- | ------- | ------------------------------------------------------------- |
|
||||||
| `--template <name>` | `basic` | Demo template to send |
|
| `--template <name>` | `basic` | Demo template to send |
|
||||||
| `--to <refs>` | — | Comma-separated notifier refs to target |
|
| `--to <refs>` | — | Comma-separated notifier refs to target |
|
||||||
| `--all` | — | Send to all configured, default, and routed notifier refs |
|
| `--all` | — | Send to all configured, default, and routed notifier refs |
|
||||||
|
|
@ -210,7 +211,7 @@ No flags.
|
||||||
> List all sessions
|
> List all sessions
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| -------------------- | ------- | ----------------------------- |
|
||||||
| `-p, --project <id>` | — | Filter to one project |
|
| `-p, --project <id>` | — | Filter to one project |
|
||||||
| `-a, --all` | — | Include orchestrator sessions |
|
| `-a, --all` | — | Include orchestrator sessions |
|
||||||
| `--json` | — | Output JSON |
|
| `--json` | — | Output JSON |
|
||||||
|
|
@ -226,7 +227,7 @@ No flags. Attaches via `tmux attach-session`. Only meaningful with `runtime: tmu
|
||||||
> Kill a session and remove its worktree
|
> Kill a session and remove its worktree
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ----------------- | ------- | ---------------------------------- |
|
||||||
| `--purge-session` | — | Delete the mapped OpenCode session |
|
| `--purge-session` | — | Delete the mapped OpenCode session |
|
||||||
|
|
||||||
### `ao session cleanup`
|
### `ao session cleanup`
|
||||||
|
|
@ -234,7 +235,7 @@ No flags. Attaches via `tmux attach-session`. Only meaningful with `runtime: tmu
|
||||||
> Kill sessions where PR is merged or issue is closed
|
> Kill sessions where PR is merged or issue is closed
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| -------------------- | ------- | ----------------------------- |
|
||||||
| `-p, --project <id>` | — | Filter to one project |
|
| `-p, --project <id>` | — | Filter to one project |
|
||||||
| `--dry-run` | — | Show what would be cleaned up |
|
| `--dry-run` | — | Show what would be cleaned up |
|
||||||
|
|
||||||
|
|
@ -243,7 +244,7 @@ No flags. Attaches via `tmux attach-session`. Only meaningful with `runtime: tmu
|
||||||
> Attach an existing PR to a session
|
> Attach an existing PR to a session
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| -------------------- | ------- | ------------------------------------------------- |
|
||||||
| `--assign-on-github` | — | Assign the PR to the authenticated user on GitHub |
|
| `--assign-on-github` | — | Assign the PR to the authenticated user on GitHub |
|
||||||
|
|
||||||
If `[session]` is omitted, falls back to `AO_SESSION_NAME` / `AO_SESSION` env vars.
|
If `[session]` is omitted, falls back to `AO_SESSION_NAME` / `AO_SESSION` env vars.
|
||||||
|
|
@ -259,7 +260,7 @@ No flags. Relaunches the agent inside the same worktree and rewires session meta
|
||||||
> Re-discover and persist OpenCode session mapping for an AO session
|
> Re-discover and persist OpenCode session mapping for an AO session
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ------------- | ------- | ------------------------ |
|
||||||
| `-f, --force` | — | Override a stale mapping |
|
| `-f, --force` | — | Override a stale mapping |
|
||||||
|
|
||||||
## Setup subcommands
|
## Setup subcommands
|
||||||
|
|
@ -270,7 +271,7 @@ mode, AO asks which notification priorities the notifier should receive before
|
||||||
writing `notificationRouting`.
|
writing `notificationRouting`.
|
||||||
|
|
||||||
| Command | Purpose |
|
| Command | Purpose |
|
||||||
|---|---|
|
| ------------------------------- | ------------------------------------------------------ |
|
||||||
| `ao setup dashboard` | Configure dashboard notification retention and routing |
|
| `ao setup dashboard` | Configure dashboard notification retention and routing |
|
||||||
| `ao setup desktop` | Configure native desktop notifications |
|
| `ao setup desktop` | Configure native desktop notifications |
|
||||||
| `ao setup webhook` | Configure a generic HTTP webhook |
|
| `ao setup webhook` | Configure a generic HTTP webhook |
|
||||||
|
|
@ -288,7 +289,7 @@ writing `notificationRouting`.
|
||||||
> Connect AO notifications to an OpenClaw gateway
|
> Connect AO notifications to an OpenClaw gateway
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ------------------------------- | --------------------------- | ---------------------------------------------------------------------------------- |
|
||||||
| `--url <url>` | — | OpenClaw webhook URL |
|
| `--url <url>` | — | OpenClaw webhook URL |
|
||||||
| `--token <token>` | — | Remote/manual fallback; local setup should read `hooks.token` from OpenClaw config |
|
| `--token <token>` | — | Remote/manual fallback; local setup should read `hooks.token` from OpenClaw config |
|
||||||
| `--openclaw-config-path <path>` | `~/.openclaw/openclaw.json` | OpenClaw config path that contains `hooks.token` |
|
| `--openclaw-config-path <path>` | `~/.openclaw/openclaw.json` | OpenClaw config path that contains `hooks.token` |
|
||||||
|
|
@ -308,7 +309,7 @@ Interactive mode (TTY) uses `@clack/prompts` with review/change steps for the ga
|
||||||
> List bundled marketplace plugins
|
> List bundled marketplace plugins
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| --------------- | ------- | ----------------------------------------------------------------------------------------- |
|
||||||
| `--installed` | — | Only show plugins present in your current config |
|
| `--installed` | — | Only show plugins present in your current config |
|
||||||
| `--type <slot>` | — | Filter by slot: `agent`, `runtime`, `tracker`, `scm`, `notifier`, `workspace`, `terminal` |
|
| `--type <slot>` | — | Filter by slot: `agent`, `runtime`, `tracker`, `scm`, `notifier`, `workspace`, `terminal` |
|
||||||
| `--refresh` | — | Re-fetch the marketplace catalog |
|
| `--refresh` | — | Re-fetch the marketplace catalog |
|
||||||
|
|
@ -324,7 +325,7 @@ No flags.
|
||||||
> Scaffold a new AO plugin package
|
> Scaffold a new AO plugin package
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ----------------------- | ------- | ------------------------------- |
|
||||||
| `--name <name>` | prompt | Plugin name |
|
| `--name <name>` | prompt | Plugin name |
|
||||||
| `--slot <slot>` | prompt | Which slot it implements |
|
| `--slot <slot>` | prompt | Which slot it implements |
|
||||||
| `--description <text>` | prompt | Manifest description |
|
| `--description <text>` | prompt | Manifest description |
|
||||||
|
|
@ -337,7 +338,7 @@ No flags.
|
||||||
> Install a plugin into the current config
|
> Install a plugin into the current config
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ------------------------------- | --------------------------- | ----------------------------------------------------------------- |
|
||||||
| `--url <url>` | — | Passed to `ao setup openclaw` when installing `notifier-openclaw` |
|
| `--url <url>` | — | Passed to `ao setup openclaw` when installing `notifier-openclaw` |
|
||||||
| `--token <token>` | — | Remote/manual OpenClaw token fallback |
|
| `--token <token>` | — | Remote/manual OpenClaw token fallback |
|
||||||
| `--openclaw-config-path <path>` | `~/.openclaw/openclaw.json` | OpenClaw config path that contains `hooks.token` |
|
| `--openclaw-config-path <path>` | `~/.openclaw/openclaw.json` | OpenClaw config path that contains `hooks.token` |
|
||||||
|
|
@ -349,7 +350,7 @@ No flags.
|
||||||
> Update installer-managed plugins in the current config
|
> Update installer-managed plugins in the current config
|
||||||
|
|
||||||
| Flag | Default | Purpose |
|
| Flag | Default | Purpose |
|
||||||
|---|---|---|
|
| ------- | ------- | -------------------------- |
|
||||||
| `--all` | — | Update all managed plugins |
|
| `--all` | — | Update all managed plugins |
|
||||||
|
|
||||||
Requires either `[reference]` or `--all`.
|
Requires either `[reference]` or `--all`.
|
||||||
|
|
@ -365,7 +366,7 @@ No flags.
|
||||||
A few env vars affect every command:
|
A few env vars affect every command:
|
||||||
|
|
||||||
| Variable | Purpose |
|
| Variable | Purpose |
|
||||||
|---|---|
|
| ----------------------------------- | ------------------------------------------------------------------------------------ |
|
||||||
| `AO_DATA_DIR` | Override the `~/.agent-orchestrator` base directory. |
|
| `AO_DATA_DIR` | Override the `~/.agent-orchestrator` base directory. |
|
||||||
| `AO_LOG_LEVEL` | `error` · `warn` · `info` · `debug` · `trace` |
|
| `AO_LOG_LEVEL` | `error` · `warn` · `info` · `debug` · `trace` |
|
||||||
| `AO_CONFIG_PATH` | Absolute path to a specific `agent-orchestrator.yaml`. Overrides CWD-based search. |
|
| `AO_CONFIG_PATH` | Absolute path to a specific `agent-orchestrator.yaml`. Overrides CWD-based search. |
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ AO uses configuration in two layers:
|
||||||
Most users should let `ao start` create the registry entry, then edit the local `agent-orchestrator.yaml` in the project when they want to change behavior.
|
Most users should let `ao start` create the registry entry, then edit the local `agent-orchestrator.yaml` in the project when they want to change behavior.
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
The old wrapped format with a top-level `projects:` block is still understood for compatibility, but new project-local configs should be flat. Do not put identity fields like `path`, `projectId`, `storageKey`, or `originUrl` in a local project config.
|
The old wrapped format with a top-level `projects:` block is still understood for compatibility, but new project-local
|
||||||
|
configs should be flat. Do not put identity fields like `path`, `projectId`, `storageKey`, or `originUrl` in a local
|
||||||
|
project config.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## What AO Creates
|
## What AO Creates
|
||||||
|
|
@ -73,7 +75,7 @@ agentRules: |
|
||||||
## What To Edit
|
## What To Edit
|
||||||
|
|
||||||
| Goal | Edit |
|
| Goal | Edit |
|
||||||
|------|------|
|
| ------------------------------------------------ | ---------------------------------------------- |
|
||||||
| Change the default agent or model | Local `agent-orchestrator.yaml` |
|
| Change the default agent or model | Local `agent-orchestrator.yaml` |
|
||||||
| Add `.env` or tool config files to each worktree | Local `symlinks` |
|
| Add `.env` or tool config files to each worktree | Local `symlinks` |
|
||||||
| Run setup commands before the agent starts | Local `postCreate` |
|
| Run setup commands before the agent starts | Local `postCreate` |
|
||||||
|
|
@ -147,7 +149,7 @@ For normal use, run AO from inside the repository and keep `agent-orchestrator.y
|
||||||
## Top-Level Global Keys
|
## Top-Level Global Keys
|
||||||
|
|
||||||
| Key | Default | Purpose |
|
| Key | Default | Purpose |
|
||||||
|-----|---------|---------|
|
| --------------------- | ------------------------- | ------------------------------------------------------------ |
|
||||||
| `port` | `3000` | Dashboard HTTP port |
|
| `port` | `3000` | Dashboard HTTP port |
|
||||||
| `terminalPort` | auto from `14800` | tmux terminal WebSocket port |
|
| `terminalPort` | auto from `14800` | tmux terminal WebSocket port |
|
||||||
| `directTerminalPort` | auto from `14801` | direct PTY WebSocket port |
|
| `directTerminalPort` | auto from `14801` | direct PTY WebSocket port |
|
||||||
|
|
@ -172,7 +174,19 @@ ls ~/.agent-orchestrator
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Projects" href="/docs/configuration/projects" description="Per-project behavior settings: agents, workspaces, rules, setup, trackers, and SCM." />
|
<Card
|
||||||
<Card title="Reactions" href="/docs/configuration/reactions" description="Control what AO does when CI fails, reviews arrive, or agents need help." />
|
title="Projects"
|
||||||
<Card title="Remote access" href="/docs/configuration/remote-access" description="Access the dashboard from another machine without exposing it publicly." />
|
href="/docs/configuration/projects"
|
||||||
|
description="Per-project behavior settings: agents, workspaces, rules, setup, trackers, and SCM."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Reactions"
|
||||||
|
href="/docs/configuration/reactions"
|
||||||
|
description="Control what AO does when CI fails, reviews arrive, or agents need help."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Remote access"
|
||||||
|
href="/docs/configuration/remote-access"
|
||||||
|
description="Access the dashboard from another machine without exposing it publicly."
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ agent: claude-code
|
||||||
Built-in agents:
|
Built-in agents:
|
||||||
|
|
||||||
| Agent | Use when |
|
| Agent | Use when |
|
||||||
|-------|----------|
|
| ------------- | ------------------------------------------------------ |
|
||||||
| `claude-code` | You want the default, most tested path |
|
| `claude-code` | You want the default, most tested path |
|
||||||
| `codex` | You want OpenAI Codex CLI sessions |
|
| `codex` | You want OpenAI Codex CLI sessions |
|
||||||
| `aider` | You already use Aider workflows |
|
| `aider` | You already use Aider workflows |
|
||||||
|
|
@ -52,7 +52,7 @@ agentConfig:
|
||||||
`permissions` accepts:
|
`permissions` accepts:
|
||||||
|
|
||||||
| Value | Behavior |
|
| Value | Behavior |
|
||||||
|-------|----------|
|
| ---------------- | ----------------------------------------------------- |
|
||||||
| `permissionless` | Let the agent edit and run commands without prompting |
|
| `permissionless` | Let the agent edit and run commands without prompting |
|
||||||
| `default` | Use the agent tool's normal permission behavior |
|
| `default` | Use the agent tool's normal permission behavior |
|
||||||
| `auto-edit` | Auto-approve edits, ask for other actions |
|
| `auto-edit` | Auto-approve edits, ask for other actions |
|
||||||
|
|
@ -69,7 +69,7 @@ runtime: tmux
|
||||||
```
|
```
|
||||||
|
|
||||||
| Runtime | Use when |
|
| Runtime | Use when |
|
||||||
|---------|----------|
|
| --------- | ----------------------------------------------------------------------------------------------- |
|
||||||
| `tmux` | You want persistent sessions that survive dashboard reloads and can be attached from a terminal |
|
| `tmux` | You want persistent sessions that survive dashboard reloads and can be attached from a terminal |
|
||||||
| `process` | You want a lighter direct process runtime and do not need tmux persistence |
|
| `process` | You want a lighter direct process runtime and do not need tmux persistence |
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ workspace: worktree
|
||||||
```
|
```
|
||||||
|
|
||||||
| Workspace | Use when |
|
| Workspace | Use when |
|
||||||
|-----------|----------|
|
| ---------- | ----------------------------------------------------------------- |
|
||||||
| `worktree` | Default. Fast, disk-efficient, creates a git worktree per session |
|
| `worktree` | Default. Fast, disk-efficient, creates a git worktree per session |
|
||||||
| `clone` | Slower, but gives each session a separate clone |
|
| `clone` | Slower, but gives each session a separate clone |
|
||||||
|
|
||||||
|
|
@ -173,7 +173,7 @@ scm:
|
||||||
Built-in trackers:
|
Built-in trackers:
|
||||||
|
|
||||||
| Plugin | Purpose |
|
| Plugin | Purpose |
|
||||||
|--------|---------|
|
| -------- | ------------- |
|
||||||
| `github` | GitHub issues |
|
| `github` | GitHub issues |
|
||||||
| `gitlab` | GitLab issues |
|
| `gitlab` | GitLab issues |
|
||||||
| `linear` | Linear issues |
|
| `linear` | Linear issues |
|
||||||
|
|
@ -181,7 +181,7 @@ Built-in trackers:
|
||||||
Built-in SCM plugins:
|
Built-in SCM plugins:
|
||||||
|
|
||||||
| Plugin | Purpose |
|
| Plugin | Purpose |
|
||||||
|--------|---------|
|
| -------- | ---------------------------------------- |
|
||||||
| `github` | GitHub PRs, checks, reviews, merge state |
|
| `github` | GitHub PRs, checks, reviews, merge state |
|
||||||
| `gitlab` | GitLab merge requests |
|
| `gitlab` | GitLab merge requests |
|
||||||
|
|
||||||
|
|
@ -234,7 +234,7 @@ opencodeIssueSessionStrategy: reuse
|
||||||
`orchestratorSessionStrategy` accepts:
|
`orchestratorSessionStrategy` accepts:
|
||||||
|
|
||||||
| Value | Behavior |
|
| Value | Behavior |
|
||||||
|-------|----------|
|
| --------------- | ----------------------------------------------------- |
|
||||||
| `reuse` | Attach to the existing orchestrator session |
|
| `reuse` | Attach to the existing orchestrator session |
|
||||||
| `delete` | Delete the old session and start a new one |
|
| `delete` | Delete the old session and start a new one |
|
||||||
| `ignore` | Leave the old session and start another |
|
| `ignore` | Leave the old session and start another |
|
||||||
|
|
@ -249,7 +249,7 @@ opencodeIssueSessionStrategy: reuse
|
||||||
These fields are valid in a local project config:
|
These fields are valid in a local project config:
|
||||||
|
|
||||||
| Field | Type | Purpose |
|
| Field | Type | Purpose |
|
||||||
|-------|------|---------|
|
| ------------------------------ | ---------- | -------------------------------------------- |
|
||||||
| `repo` | `string` | Optional legacy/local repo slug |
|
| `repo` | `string` | Optional legacy/local repo slug |
|
||||||
| `defaultBranch` | `string` | Branch PRs target, usually `main` |
|
| `defaultBranch` | `string` | Branch PRs target, usually `main` |
|
||||||
| `agent` | `string` | Default worker agent |
|
| `agent` | `string` | Default worker agent |
|
||||||
|
|
@ -292,7 +292,19 @@ Make sure the corresponding CLI or token is authenticated for the plugin you use
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Reactions" href="/docs/configuration/reactions" description="Configure what AO does when CI, reviews, or agent state changes need attention." />
|
<Card
|
||||||
<Card title="Remote access" href="/docs/configuration/remote-access" description="Open the dashboard from another machine without exposing it publicly." />
|
title="Reactions"
|
||||||
<Card title="Plugin docs" href="/docs/plugins" description="Agent, tracker, SCM, notifier, runtime, terminal, and workspace plugins." />
|
href="/docs/configuration/reactions"
|
||||||
|
description="Configure what AO does when CI, reviews, or agent state changes need attention."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Remote access"
|
||||||
|
href="/docs/configuration/remote-access"
|
||||||
|
description="Open the dashboard from another machine without exposing it publicly."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Plugin docs"
|
||||||
|
href="/docs/plugins"
|
||||||
|
description="Agent, tracker, SCM, notifier, runtime, terminal, and workspace plugins."
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -58,13 +58,14 @@ reactions:
|
||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="warning">
|
<Callout type="warning">
|
||||||
`auto-merge` is currently an intent flag, not a bypass. AO does not ignore branch protection, reviews, or failing checks.
|
`auto-merge` is currently an intent flag, not a bypass. AO does not ignore branch protection, reviews, or failing
|
||||||
|
checks.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Default Reactions
|
## Default Reactions
|
||||||
|
|
||||||
| Key | When it fires | Default action | Notes |
|
| Key | When it fires | Default action | Notes |
|
||||||
|-----|---------------|----------------|-------|
|
| -------------------- | ------------------------------------------- | --------------- | --------------------------------------------------- |
|
||||||
| `ci-failed` | CI check fails | `send-to-agent` | Sends the agent a recovery prompt |
|
| `ci-failed` | CI check fails | `send-to-agent` | Sends the agent a recovery prompt |
|
||||||
| `changes-requested` | Human requests changes | `send-to-agent` | Sends review feedback to the agent |
|
| `changes-requested` | Human requests changes | `send-to-agent` | Sends review feedback to the agent |
|
||||||
| `bugbot-comments` | Known automation leaves actionable feedback | `send-to-agent` | Keeps bot feedback separate from human reviews |
|
| `bugbot-comments` | Known automation leaves actionable feedback | `send-to-agent` | Keeps bot feedback separate from human reviews |
|
||||||
|
|
@ -79,7 +80,7 @@ reactions:
|
||||||
## Action Types
|
## Action Types
|
||||||
|
|
||||||
| Action | Meaning |
|
| Action | Meaning |
|
||||||
|--------|---------|
|
| --------------- | ----------------------------------------------------------- |
|
||||||
| `send-to-agent` | Sends `message` into the running agent session |
|
| `send-to-agent` | Sends `message` into the running agent session |
|
||||||
| `notify` | Routes an event to configured notifiers |
|
| `notify` | Routes an event to configured notifiers |
|
||||||
| `auto-merge` | Reserved merge intent; currently routes like a notification |
|
| `auto-merge` | Reserved merge intent; currently routes like a notification |
|
||||||
|
|
@ -91,7 +92,7 @@ reactions:
|
||||||
Each reaction accepts:
|
Each reaction accepts:
|
||||||
|
|
||||||
| Field | Type | Purpose |
|
| Field | Type | Purpose |
|
||||||
|-------|------|---------|
|
| ---------------- | ------------------------------------------- | ---------------------------------------------------------- |
|
||||||
| `auto` | `boolean` | Whether AO should perform the automated action |
|
| `auto` | `boolean` | Whether AO should perform the automated action |
|
||||||
| `action` | `send-to-agent` \| `notify` \| `auto-merge` | What AO does when the reaction fires |
|
| `action` | `send-to-agent` \| `notify` \| `auto-merge` | What AO does when the reaction fires |
|
||||||
| `message` | `string` | Message sent to the agent or included in notification text |
|
| `message` | `string` | Message sent to the agent or included in notification text |
|
||||||
|
|
@ -173,7 +174,7 @@ ao review-check <session-id>
|
||||||
## Event Reference
|
## Event Reference
|
||||||
|
|
||||||
| Event | Reaction |
|
| Event | Reaction |
|
||||||
|-------|----------|
|
| -------------------------- | -------------------- |
|
||||||
| `ci.failing` | `ci-failed` |
|
| `ci.failing` | `ci-failed` |
|
||||||
| `review.changes_requested` | `changes-requested` |
|
| `review.changes_requested` | `changes-requested` |
|
||||||
| `automated_review.found` | `bugbot-comments` |
|
| `automated_review.found` | `bugbot-comments` |
|
||||||
|
|
@ -188,6 +189,14 @@ ao review-check <session-id>
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Projects" href="/docs/configuration/projects" description="Apply reaction overrides to one project." />
|
<Card title="Projects" href="/docs/configuration/projects" description="Apply reaction overrides to one project." />
|
||||||
<Card title="Webhook notifier" href="/docs/plugins/notifiers/webhook" description="Send AO events to any HTTPS endpoint." />
|
<Card
|
||||||
<Card title="Configuration" href="/docs/configuration" description="Understand global registry vs local project config." />
|
title="Webhook notifier"
|
||||||
|
href="/docs/plugins/notifiers/webhook"
|
||||||
|
description="Send AO events to any HTTPS endpoint."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Configuration"
|
||||||
|
href="/docs/configuration"
|
||||||
|
description="Understand global registry vs local project config."
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ If you want to expose AO over a domain name with TLS, or place it behind an auth
|
||||||
### Environment variables for proxied setups
|
### Environment variables for proxied setups
|
||||||
|
|
||||||
| Variable | Purpose |
|
| Variable | Purpose |
|
||||||
|----------|---------|
|
| ------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
||||||
| `HOST=0.0.0.0` | Bind the Next.js dashboard to all interfaces |
|
| `HOST=0.0.0.0` | Bind the Next.js dashboard to all interfaces |
|
||||||
| `TERMINAL_PORT` | Override the tmux WS server port (server-side) |
|
| `TERMINAL_PORT` | Override the tmux WS server port (server-side) |
|
||||||
| `DIRECT_TERMINAL_PORT` | Override the direct PTY WS server port (server-side) |
|
| `DIRECT_TERMINAL_PORT` | Override the direct PTY WS server port (server-side) |
|
||||||
|
|
@ -270,7 +270,7 @@ For more on project identity, local config, and runtime data, see [Configuration
|
||||||
## Port reference
|
## Port reference
|
||||||
|
|
||||||
| Port | Default | Config key | Env var override | Purpose |
|
| Port | Default | Config key | Env var override | Purpose |
|
||||||
|------|---------|------------|-----------------|---------|
|
| ------- | ------------------ | -------------------- | ---------------------- | ------------------------------------ |
|
||||||
| `3000` | dashboard HTTP | `port` | `PORT` | Next.js app + API routes |
|
| `3000` | dashboard HTTP | `port` | `PORT` | Next.js app + API routes |
|
||||||
| `14800` | tmux terminal WS | `terminalPort` | `TERMINAL_PORT` | WebSocket for tmux-attached terminal |
|
| `14800` | tmux terminal WS | `terminalPort` | `TERMINAL_PORT` | WebSocket for tmux-attached terminal |
|
||||||
| `14801` | direct terminal WS | `directTerminalPort` | `DIRECT_TERMINAL_PORT` | WebSocket for direct PTY terminal |
|
| `14801` | direct terminal WS | `directTerminalPort` | `DIRECT_TERMINAL_PORT` | WebSocket for direct PTY terminal |
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ http://localhost:3000
|
||||||
The main dashboard groups sessions by what you need to do next.
|
The main dashboard groups sessions by what you need to do next.
|
||||||
|
|
||||||
| Column | What it means | What you usually do |
|
| Column | What it means | What you usually do |
|
||||||
|--------|---------------|---------------------|
|
| ----------- | -------------------------------------------------- | ----------------------------------------- |
|
||||||
| **Working** | Agent is actively coding or running commands | Let it run |
|
| **Working** | Agent is actively coding or running commands | Let it run |
|
||||||
| **Pending** | Session exists but has not started useful work yet | Check if it stays here too long |
|
| **Pending** | Session exists but has not started useful work yet | Check if it stays here too long |
|
||||||
| **Review** | PR is open and waiting for review | Review the PR or wait for CI |
|
| **Review** | PR is open and waiting for review | Review the PR or wait for CI |
|
||||||
|
|
@ -59,7 +59,7 @@ Open `/prs` to see every PR created by AO-managed sessions.
|
||||||
The PR page lets you filter by:
|
The PR page lets you filter by:
|
||||||
|
|
||||||
| Tab | Shows |
|
| Tab | Shows |
|
||||||
|-----|-------|
|
| ---------- | ---------------------------- |
|
||||||
| **All** | Open, merged, and closed PRs |
|
| **All** | Open, merged, and closed PRs |
|
||||||
| **Open** | PRs still in progress |
|
| **Open** | PRs still in progress |
|
||||||
| **Merged** | Completed PRs |
|
| **Merged** | Completed PRs |
|
||||||
|
|
@ -84,7 +84,7 @@ Use the terminal to inspect what the agent is doing, recover from stuck prompts,
|
||||||
AO has two terminal backends:
|
AO has two terminal backends:
|
||||||
|
|
||||||
| Backend | Used when | Default port |
|
| Backend | Used when | Default port |
|
||||||
|---------|-----------|--------------|
|
| -------------------- | ------------------ | ------------ |
|
||||||
| tmux WebSocket mux | `runtime: tmux` | `14800` |
|
| tmux WebSocket mux | `runtime: tmux` | `14800` |
|
||||||
| direct PTY WebSocket | `runtime: process` | `14801` |
|
| direct PTY WebSocket | `runtime: process` | `14801` |
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ The favicon and document title also change when sessions need attention, so you
|
||||||
## Routes
|
## Routes
|
||||||
|
|
||||||
| Route | Use for |
|
| Route | Use for |
|
||||||
|-------|---------|
|
| ------------------------------------- | ----------------------------------------- |
|
||||||
| `/` | Project overview or current project board |
|
| `/` | Project overview or current project board |
|
||||||
| `/projects/{projectId}` | One project's board |
|
| `/projects/{projectId}` | One project's board |
|
||||||
| `/projects/{projectId}/settings` | Project behavior settings |
|
| `/projects/{projectId}/settings` | Project behavior settings |
|
||||||
|
|
@ -136,7 +136,7 @@ Internal API routes such as `/api/events`, `/api/sessions`, `/api/projects`, and
|
||||||
The full dashboard experience uses three ports:
|
The full dashboard experience uses three ports:
|
||||||
|
|
||||||
| Service | Default | Config |
|
| Service | Default | Config |
|
||||||
|---------|---------|--------|
|
| ----------------------- | ----------------- | ---------------------------------------------- |
|
||||||
| Dashboard HTTP | `3000` | `port` or `PORT` |
|
| Dashboard HTTP | `3000` | `port` or `PORT` |
|
||||||
| tmux terminal WebSocket | auto from `14800` | `terminalPort` or `TERMINAL_PORT` |
|
| tmux terminal WebSocket | auto from `14800` | `terminalPort` or `TERMINAL_PORT` |
|
||||||
| direct PTY WebSocket | auto from `14801` | `directTerminalPort` or `DIRECT_TERMINAL_PORT` |
|
| direct PTY WebSocket | auto from `14801` | `directTerminalPort` or `DIRECT_TERMINAL_PORT` |
|
||||||
|
|
@ -164,6 +164,14 @@ Open the session detail page and send a short, direct instruction. The agent rec
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Quickstart" href="/docs/quickstart" description="Start AO, spawn a session, and handle the first PR." />
|
<Card title="Quickstart" href="/docs/quickstart" description="Start AO, spawn a session, and handle the first PR." />
|
||||||
<Card title="Configuration" href="/docs/configuration" description="Understand global registry and local project settings." />
|
<Card
|
||||||
<Card title="Remote access" href="/docs/configuration/remote-access" description="Open the dashboard from another device safely." />
|
title="Configuration"
|
||||||
|
href="/docs/configuration"
|
||||||
|
description="Understand global registry and local project settings."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Remote access"
|
||||||
|
href="/docs/configuration/remote-access"
|
||||||
|
description="Open the dashboard from another device safely."
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,19 @@ projects:
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Configuration Reference" href="/docs/configuration" description="Full reference for every field in agent-orchestrator.yaml." />
|
<Card
|
||||||
<Card title="Plugins" href="/docs/plugins" description="Browse available runtime, agent, tracker, SCM, notifier, and terminal plugins." />
|
title="Configuration Reference"
|
||||||
<Card title="Guides" href="/docs/guides" description="Step-by-step workflows for common setups and automation patterns." />
|
href="/docs/configuration"
|
||||||
|
description="Full reference for every field in agent-orchestrator.yaml."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Plugins"
|
||||||
|
href="/docs/plugins"
|
||||||
|
description="Browse available runtime, agent, tracker, SCM, notifier, and terminal plugins."
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Guides"
|
||||||
|
href="/docs/guides"
|
||||||
|
description="Step-by-step workflows for common setups and automation patterns."
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -10,53 +10,63 @@ import { Accordions, Accordion } from "fumadocs-ui/components/accordion";
|
||||||
Tabs are fine for one or two. AO handles the bookkeeping once you have more than that: per-session worktrees, per-session branches, automatic CI/review reactions, one dashboard, cost tracking, and a state machine that knows what each agent is doing.
|
Tabs are fine for one or two. AO handles the bookkeeping once you have more than that: per-session worktrees, per-session branches, automatic CI/review reactions, one dashboard, cost tracking, and a state machine that knows what each agent is doing.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Do I have to use Claude Code?">
|
<Accordion title="Do I have to use Claude Code?">
|
||||||
No. AO supports Claude Code, Codex, Cursor, Aider, and OpenCode today. Set `agent:` per project (or per spawn with `--agent`). See [Agents](/docs/plugins/agents).
|
No. AO supports Claude Code, Codex, Cursor, Aider, and OpenCode today. Set `agent:` per project (or per spawn with
|
||||||
</Accordion>
|
`--agent`). See [Agents](/docs/plugins/agents).
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Does AO auto-merge?">
|
<Accordion title="Does AO auto-merge?">
|
||||||
No, intentionally. AO will get a PR to a mergeable state — CI green, approvals satisfied — and stop. The merge is your call.
|
No, intentionally. AO will get a PR to a mergeable state — CI green, approvals satisfied — and stop. The merge is your
|
||||||
</Accordion>
|
call.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Does AO need webhooks?">
|
<Accordion title="Does AO need webhooks?">
|
||||||
No. AO polls `gh` / `glab` for PR state. Webhooks are an optional latency optimisation for the review loop — wire them up only if you need near-instant reactions.
|
No. AO polls `gh` / `glab` for PR state. Webhooks are an optional latency optimisation for the review loop — wire them
|
||||||
</Accordion>
|
up only if you need near-instant reactions.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Can it use GitHub Enterprise / self-hosted GitLab?">
|
<Accordion title="Can it use GitHub Enterprise / self-hosted GitLab?">
|
||||||
Yes. `gh` and `glab` both support enterprise hosts — configure them via their own tools. For GitLab, also set `trackerConfig.host` / `scmConfig.host` in AO.
|
Yes. `gh` and `glab` both support enterprise hosts — configure them via their own tools. For GitLab, also set
|
||||||
</Accordion>
|
`trackerConfig.host` / `scmConfig.host` in AO.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Does it work on Windows?">
|
<Accordion title="Does it work on Windows?">
|
||||||
Partially — see [Platforms › Windows](/docs/platforms#windows). You need `runtime: process` instead of tmux, and desktop notifications are a no-op. Everything else (spawning, PR flow, review loop, CI recovery) works the same.
|
Partially — see [Platforms › Windows](/docs/platforms#windows). You need `runtime: process` instead of tmux, and
|
||||||
</Accordion>
|
desktop notifications are a no-op. Everything else (spawning, PR flow, review loop, CI recovery) works the same.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Where does session state live?">
|
<Accordion title="Where does session state live?">
|
||||||
`~/.agent-orchestrator/{hash}-{projectId}/`. No database. Everything is flat files you can inspect.
|
`~/.agent-orchestrator/{hash}-{projectId}/`. No database. Everything is flat files you can inspect.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Can I run AO on a remote server?">
|
<Accordion title="Can I run AO on a remote server?">
|
||||||
Yes. The dashboard is a plain Next.js app — port-forward or put it behind your reverse proxy. AO has no built-in auth, so use your usual SSO / basic-auth layer. Use `runtime: process` in containers.
|
Yes. The dashboard is a plain Next.js app — port-forward or put it behind your reverse proxy. AO has no built-in auth,
|
||||||
</Accordion>
|
so use your usual SSO / basic-auth layer. Use `runtime: process` in containers.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="How much does it cost?">
|
<Accordion title="How much does it cost?">
|
||||||
AO itself is MIT-licensed and free. Agents cost whatever their underlying API costs (Anthropic, OpenAI, etc.) — AO shows per-session cost on the dashboard for agents that report it (Claude Code today).
|
AO itself is MIT-licensed and free. Agents cost whatever their underlying API costs (Anthropic, OpenAI, etc.) — AO
|
||||||
</Accordion>
|
shows per-session cost on the dashboard for agents that report it (Claude Code today).
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="What happens to my code?">
|
<Accordion title="What happens to my code?">
|
||||||
Your code stays in your worktrees on your machine. AO doesn't upload anything — it just coordinates local processes and makes GitHub API calls through your authenticated CLIs.
|
Your code stays in your worktrees on your machine. AO doesn't upload anything — it just coordinates local processes
|
||||||
</Accordion>
|
and makes GitHub API calls through your authenticated CLIs.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Can I write my own plugin?">
|
<Accordion title="Can I write my own plugin?">
|
||||||
Yes. `ao plugin create --slot <slot> --name <name>` scaffolds a starter. Plugins are tiny Node packages exporting a manifest + `create()` function.
|
Yes. `ao plugin create --slot <slot> --name <name>` scaffolds a starter. Plugins are tiny Node packages exporting a manifest + `create()` function.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Why is everything a plugin?">
|
<Accordion title="Why is everything a plugin?">
|
||||||
Because the answer to "should AO support Linear?" (or Discord, or Codex, or …) is always yes, but bundling every integration into core makes the CLI heavy. Plugins let you pick what you need.
|
Because the answer to "should AO support Linear?" (or Discord, or Codex, or …) is always yes, but bundling every
|
||||||
</Accordion>
|
integration into core makes the CLI heavy. Plugins let you pick what you need.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Does AO modify my repo's files?">
|
<Accordion title="Does AO modify my repo's files?">
|
||||||
The agent modifies the worktree — that's the whole point. AO itself doesn't touch `main`, doesn't force-push, and doesn't merge. The session works on its own branch.
|
The agent modifies the worktree — that's the whole point. AO itself doesn't touch `main`, doesn't force-push, and
|
||||||
</Accordion>
|
doesn't merge. The session works on its own branch.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="How do I completely uninstall?">
|
<Accordion title="How do I completely uninstall?">
|
||||||
`npm uninstall -g @aoagents/ao`, then `rm -rf ~/.agent-orchestrator` if you want to wipe all session history.
|
`npm uninstall -g @aoagents/ao`, then `rm -rf ~/.agent-orchestrator` if you want to wipe all session history.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ When your PR's CI goes red, AO notices before you do and tells the agent to fix
|
||||||
|
|
||||||
1. The **SCM plugin** polls the PR's check runs via `gh pr checks` (or `glab`).
|
1. The **SCM plugin** polls the PR's check runs via `gh pr checks` (or `glab`).
|
||||||
2. The **lifecycle manager** transitions the session to `ci_failed` when any required check fails.
|
2. The **lifecycle manager** transitions the session to `ci_failed` when any required check fails.
|
||||||
3. The **agent plugin** is woken with a prompt like *"CI failed on PR #42. The failing checks are X and Y. Investigate and push a fix."*
|
3. The **agent plugin** is woken with a prompt like _"CI failed on PR #42. The failing checks are X and Y. Investigate and push a fix."_
|
||||||
4. The **notifier plugin** pings you (desktop, Slack, Discord, whatever you configured).
|
4. The **notifier plugin** pings you (desktop, Slack, Discord, whatever you configured).
|
||||||
|
|
||||||
No webhooks, no CI integration to install. The `gh` CLI you already authenticated with is doing the work.
|
No webhooks, no CI integration to install. The `gh` CLI you already authenticated with is doing the work.
|
||||||
|
|
@ -74,7 +74,7 @@ This creates a session, points it at PR #123, and (with `--assign-on-github`) as
|
||||||
|
|
||||||
When CI goes red, AO sends two distinct messages to the agent in sequence:
|
When CI goes red, AO sends two distinct messages to the agent in sequence:
|
||||||
|
|
||||||
1. **Reaction message (poll cycle N).** The lifecycle manager detects that CI transitioned to `ci_failed` and fires the `ci-failed` reaction. This sends the configured `message` (or the default: *"CI failed on PR #42…"*) to the agent via `ao send`.
|
1. **Reaction message (poll cycle N).** The lifecycle manager detects that CI transitioned to `ci_failed` and fires the `ci-failed` reaction. This sends the configured `message` (or the default: _"CI failed on PR #42…"_) to the agent via `ao send`.
|
||||||
|
|
||||||
2. **Detailed follow-up (poll cycle N+1, ~30 s later).** On the next poll cycle, AO calls `formatCIFailureMessage()` with the actual failing checks and sends a second message with each check's name, conclusion status, and a direct link to the run log. This is delivered via `sessionManager.send()` directly — it does **not** go through `executeReaction()`, so it does not consume the `ci-failed` reaction's retry budget.
|
2. **Detailed follow-up (poll cycle N+1, ~30 s later).** On the next poll cycle, AO calls `formatCIFailureMessage()` with the actual failing checks and sends a second message with each check's name, conclusion status, and a direct link to the run log. This is delivered via `sessionManager.send()` directly — it does **not** go through `executeReaction()`, so it does not consume the `ci-failed` reaction's retry budget.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,34 @@ description: Real scenarios — parallel issues, CI recovery, review loops, mult
|
||||||
These guides are task-shaped: each one covers a single thing you'd actually want AO to do for you, end to end.
|
These guides are task-shaped: each one covers a single thing you'd actually want AO to do for you, end to end.
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Per-role agents" description="Run a reasoning-heavy orchestrator and a fast worker — globally or per-project." href="/docs/guides/per-role-agents" />
|
<Card
|
||||||
<Card title="Parallel issues" description="Spawn agents on several issues at once without them stepping on each other." href="/docs/guides/parallel-issues" />
|
title="Per-role agents"
|
||||||
<Card title="CI recovery" description="How AO notices red CI and nudges the agent to fix it without you babysitting." href="/docs/guides/ci-recovery" />
|
description="Run a reasoning-heavy orchestrator and a fast worker — globally or per-project."
|
||||||
<Card title="Review loop" description="When a reviewer asks for changes, AO replays the feedback to the agent." href="/docs/guides/review-loop" />
|
href="/docs/guides/per-role-agents"
|
||||||
<Card title="Reaction recipes" description="Watch-only mode, auto-merge opt-in, custom CI messages, bot handling." href="/docs/guides/reactions" />
|
/>
|
||||||
<Card title="Multi-project" description="Run AO against several repos from one dashboard." href="/docs/guides/multi-project" />
|
<Card
|
||||||
|
title="Parallel issues"
|
||||||
|
description="Spawn agents on several issues at once without them stepping on each other."
|
||||||
|
href="/docs/guides/parallel-issues"
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="CI recovery"
|
||||||
|
description="How AO notices red CI and nudges the agent to fix it without you babysitting."
|
||||||
|
href="/docs/guides/ci-recovery"
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Review loop"
|
||||||
|
description="When a reviewer asks for changes, AO replays the feedback to the agent."
|
||||||
|
href="/docs/guides/review-loop"
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Reaction recipes"
|
||||||
|
description="Watch-only mode, auto-merge opt-in, custom CI messages, bot handling."
|
||||||
|
href="/docs/guides/reactions"
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Multi-project"
|
||||||
|
description="Run AO against several repos from one dashboard."
|
||||||
|
href="/docs/guides/multi-project"
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"title": "Guides",
|
"title": "Guides",
|
||||||
"defaultOpen": false,
|
"defaultOpen": false,
|
||||||
"pages": [
|
"pages": ["per-role-agents", "parallel-issues", "ci-recovery", "review-loop", "reactions", "multi-project"]
|
||||||
"per-role-agents",
|
|
||||||
"parallel-issues",
|
|
||||||
"ci-recovery",
|
|
||||||
"review-loop",
|
|
||||||
"reactions",
|
|
||||||
"multi-project"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,5 +93,6 @@ ao stop --all # stop everything
|
||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
Each project can have a different agent, tracker, notifier, and workspace — that's the whole point of the plugin system. Don't force uniformity; let teams use what fits.
|
Each project can have a different agent, tracker, notifier, and workspace — that's the whole point of the plugin
|
||||||
|
system. Don't force uniformity; let teams use what fits.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ AO creates two distinct sessions. Compare the PRs side by side.
|
||||||
## Isolation guarantees
|
## Isolation guarantees
|
||||||
|
|
||||||
| What's isolated | How |
|
| What's isolated | How |
|
||||||
|---|---|
|
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| File edits | Each session has its own worktree under `~/.worktrees/{projectId}/{sessionId}/`. Session metadata lives under `~/.agent-orchestrator/{hash}-{projectId}/sessions/{sessionId}` — the worktree itself is in `~/.worktrees/`. |
|
| File edits | Each session has its own worktree under `~/.worktrees/{projectId}/{sessionId}/`. Session metadata lives under `~/.agent-orchestrator/{hash}-{projectId}/sessions/{sessionId}` — the worktree itself is in `~/.worktrees/`. |
|
||||||
| Branch name | AO names the branch after the session ID, avoiding collisions |
|
| Branch name | AO names the branch after the session ID, avoiding collisions |
|
||||||
| Agent state | Each agent's native session files (Claude JSONL, Codex session, etc.) are session-scoped |
|
| Agent state | Each agent's native session files (Claude JSONL, Codex session, etc.) are session-scoped |
|
||||||
|
|
@ -79,6 +79,4 @@ ao status -p my-repo --json
|
||||||
- `ao session cleanup` — kill everything whose PR merged or issue closed (safe; archives metadata)
|
- `ao session cleanup` — kill everything whose PR merged or issue closed (safe; archives metadata)
|
||||||
- `ao session cleanup --dry-run` — preview first
|
- `ao session cleanup --dry-run` — preview first
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">Need them all gone? `ao stop --all` halts every running AO instance on this machine.</Callout>
|
||||||
Need them all gone? `ao stop --all` halts every running AO instance on this machine.
|
|
||||||
</Callout>
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ defaults:
|
||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
`defaults.orchestrator` and `defaults.worker` accept only the `agent` key at the global defaults level. To set `agentConfig` (model, permissions, etc.), use the per-project `orchestrator` and `worker` blocks described below.
|
`defaults.orchestrator` and `defaults.worker` accept only the `agent` key at the global defaults level. To set
|
||||||
|
`agentConfig` (model, permissions, etc.), use the per-project `orchestrator` and `worker` blocks described below.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Per-project override
|
## Per-project override
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@ reactions:
|
||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="warning">
|
<Callout type="warning">
|
||||||
The `auto-merge` action currently calls `notifyHuman()` internally — it does not perform a real merge. Actual merging still depends on your repository's branch protection rules and the "Allow auto-merge" setting on GitHub. AO does not bypass these gates. Treat `auto-merge` as an opt-in signal for when the SCM plugin adds real merge support.
|
The `auto-merge` action currently calls `notifyHuman()` internally — it does not perform a real merge. Actual merging
|
||||||
|
still depends on your repository's branch protection rules and the "Allow auto-merge" setting on GitHub. AO does not
|
||||||
|
bypass these gates. Treat `auto-merge` as an opt-in signal for when the SCM plugin adds real merge support.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Recipe: Custom CI failure message
|
## Recipe: Custom CI failure message
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ A single structured prompt with:
|
||||||
- A pointer to the PR head SHA
|
- A pointer to the PR head SHA
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
AO reads unresolved review threads. Once you resolve a thread on GitHub, it drops out of the next nudge — so you can thumbs-up the ones the agent addressed and only the remaining ones make it back to the agent.
|
AO reads unresolved review threads. Once you resolve a thread on GitHub, it drops out of the next nudge — so you can
|
||||||
|
thumbs-up the ones the agent addressed and only the remaining ones make it back to the agent.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Configurable behavior
|
## Configurable behavior
|
||||||
|
|
@ -71,7 +72,7 @@ Not every review comment is from a human. AO recognises a hardcoded list of know
|
||||||
**GitHub** (`scm-github`) treats the following as bots:
|
**GitHub** (`scm-github`) treats the following as bots:
|
||||||
|
|
||||||
| Login | Tool |
|
| Login | Tool |
|
||||||
|---|---|
|
| ------------------------- | -------------- |
|
||||||
| `cursor[bot]` | Cursor AI |
|
| `cursor[bot]` | Cursor AI |
|
||||||
| `github-actions[bot]` | GitHub Actions |
|
| `github-actions[bot]` | GitHub Actions |
|
||||||
| `codecov[bot]` | Codecov |
|
| `codecov[bot]` | Codecov |
|
||||||
|
|
@ -86,7 +87,7 @@ Not every review comment is from a human. AO recognises a hardcoded list of know
|
||||||
**GitLab** (`scm-gitlab`) treats the following as bots (in addition to any username matching `project_\d+_bot` or ending in `[bot]`):
|
**GitLab** (`scm-gitlab`) treats the following as bots (in addition to any username matching `project_\d+_bot` or ending in `[bot]`):
|
||||||
|
|
||||||
| Login | Tool |
|
| Login | Tool |
|
||||||
|---|---|
|
| ------------------ | --------------------- |
|
||||||
| `gitlab-bot` | GitLab built-in |
|
| `gitlab-bot` | GitLab built-in |
|
||||||
| `ghost` | Deleted / system user |
|
| `ghost` | Deleted / system user |
|
||||||
| `dependabot[bot]` | Dependabot |
|
| `dependabot[bot]` | Dependabot |
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ Agent Orchestrator (**AO**) runs AI coding agents in isolated git worktrees and
|
||||||
Use it when you have several well-scoped issues and want agents to work on them at the same time without sharing a checkout, terminal, or branch. AO starts each session, watches the agent, tracks the PR, and shows the state of every session in one dashboard.
|
Use it when you have several well-scoped issues and want agents to work on them at the same time without sharing a checkout, terminal, or branch. AO starts each session, watches the agent, tracks the PR, and shows the state of every session in one dashboard.
|
||||||
|
|
||||||
<Callout type="info" title="Fastest path">
|
<Callout type="info" title="Fastest path">
|
||||||
If you are new to AO, install it first, then run the quickstart against one small issue. Start with [Installation](/docs/installation), then [Quickstart](/docs/quickstart).
|
If you are new to AO, install it first, then run the quickstart against one small issue. Start with
|
||||||
|
[Installation](/docs/installation), then [Quickstart](/docs/quickstart).
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## What AO Is For
|
## What AO Is For
|
||||||
|
|
@ -30,7 +31,7 @@ AO works best for issues that have a clear outcome: failing tests, small feature
|
||||||
AO does not replace review. It helps agents keep working, but you still decide what gets merged.
|
AO does not replace review. It helps agents keep working, but you still decide what gets merged.
|
||||||
|
|
||||||
| AO handles | You still handle |
|
| AO handles | You still handle |
|
||||||
| --- | --- |
|
| ---------------------------------- | -------------------------------------------- |
|
||||||
| Creating isolated workspaces | Choosing good issues |
|
| Creating isolated workspaces | Choosing good issues |
|
||||||
| Launching and monitoring agents | Reviewing code and behavior |
|
| Launching and monitoring agents | Reviewing code and behavior |
|
||||||
| Tracking PR, CI, and review status | Deciding when to merge |
|
| Tracking PR, CI, and review status | Deciding when to merge |
|
||||||
|
|
@ -49,7 +50,7 @@ The dashboard shows this lifecycle as session cards. Open a card to see the live
|
||||||
AO is built from plugins. The default setup works out of the box for common GitHub workflows, and you can swap pieces when your setup is different.
|
AO is built from plugins. The default setup works out of the box for common GitHub workflows, and you can swap pieces when your setup is different.
|
||||||
|
|
||||||
| Plugin slot | What it controls | Examples |
|
| Plugin slot | What it controls | Examples |
|
||||||
| --- | --- | --- |
|
| ----------- | --------------------------------- | ------------------------------------------- |
|
||||||
| Agent | Which coding tool writes changes | Claude Code, Codex, Cursor, Aider, OpenCode |
|
| Agent | Which coding tool writes changes | Claude Code, Codex, Cursor, Aider, OpenCode |
|
||||||
| Runtime | How the agent process runs | tmux, child process |
|
| Runtime | How the agent process runs | tmux, child process |
|
||||||
| Workspace | Where code is checked out | git worktree, full clone |
|
| Workspace | Where code is checked out | git worktree, full clone |
|
||||||
|
|
@ -65,16 +66,38 @@ Most users start with the defaults and only edit `agent-orchestrator.yaml` when
|
||||||
macos="full"
|
macos="full"
|
||||||
linux="full"
|
linux="full"
|
||||||
windows="partial"
|
windows="partial"
|
||||||
note={<>Windows support is actively improving. Use the process runtime instead of tmux by setting <code>defaults.runtime: process</code> in <code>agent-orchestrator.yaml</code>. See <a href="/docs/platforms">Platforms</a> for details.</>}
|
note={
|
||||||
|
<>
|
||||||
|
Windows support is actively improving. Use the process runtime instead of tmux by setting{" "}
|
||||||
|
<code>defaults.runtime: process</code> in <code>agent-orchestrator.yaml</code>. See{" "}
|
||||||
|
<a href="/docs/platforms">Platforms</a> for details.
|
||||||
|
</>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
## Where To Go Next
|
## Where To Go Next
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Installation" description="Install AO, authenticate your tools, and run the doctor check." href="/docs/installation" />
|
<Card
|
||||||
|
title="Installation"
|
||||||
|
description="Install AO, authenticate your tools, and run the doctor check."
|
||||||
|
href="/docs/installation"
|
||||||
|
/>
|
||||||
<Card title="Quickstart" description="Spawn one agent on one issue and watch it open a PR." href="/docs/quickstart" />
|
<Card title="Quickstart" description="Spawn one agent on one issue and watch it open a PR." href="/docs/quickstart" />
|
||||||
<Card title="Configuration" description="Understand the `agent-orchestrator.yaml` file." href="/docs/configuration" />
|
<Card title="Configuration" description="Understand the `agent-orchestrator.yaml` file." href="/docs/configuration" />
|
||||||
<Card title="Guides" description="Run parallel issues, recover from CI failures, and handle review loops." href="/docs/guides" />
|
<Card
|
||||||
<Card title="Plugins" description="See which agents, runtimes, trackers, SCMs, terminals, and notifiers are available." href="/docs/plugins" />
|
title="Guides"
|
||||||
<Card title="Troubleshooting" description="Fix common install, dashboard, runtime, and session problems." href="/docs/troubleshooting" />
|
description="Run parallel issues, recover from CI failures, and handle review loops."
|
||||||
|
href="/docs/guides"
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Plugins"
|
||||||
|
description="See which agents, runtimes, trackers, SCMs, terminals, and notifiers are available."
|
||||||
|
href="/docs/plugins"
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Troubleshooting"
|
||||||
|
description="Fix common install, dashboard, runtime, and session problems."
|
||||||
|
href="/docs/troubleshooting"
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ import { Accordions, Accordion } from "fumadocs-ui/components/accordion";
|
||||||
This page gets your machine ready to run Agent Orchestrator. By the end, the `ao` command should be on your `PATH`, your source-control CLI should be authenticated, and `ao doctor` should be able to explain what is ready or missing.
|
This page gets your machine ready to run Agent Orchestrator. By the end, the `ao` command should be on your `PATH`, your source-control CLI should be authenticated, and `ao doctor` should be able to explain what is ready or missing.
|
||||||
|
|
||||||
<Callout type="info" title="What AO installs">
|
<Callout type="info" title="What AO installs">
|
||||||
The published package is `@aoagents/ao`. It provides the global `ao` command and includes the built-in CLI, dashboard, and plugin packages.
|
The published package is `@aoagents/ao`. It provides the global `ao` command and includes the built-in CLI, dashboard,
|
||||||
|
and plugin packages.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
@ -20,13 +21,18 @@ This page gets your machine ready to run Agent Orchestrator. By the end, the `ao
|
||||||
macos="full"
|
macos="full"
|
||||||
linux="full"
|
linux="full"
|
||||||
windows="partial"
|
windows="partial"
|
||||||
note={<>Windows support is actively improving. Use <code>defaults.runtime: process</code> instead of tmux. See <a href="/docs/platforms#windows">Platforms</a>.</>}
|
note={
|
||||||
|
<>
|
||||||
|
Windows support is actively improving. Use <code>defaults.runtime: process</code> instead of tmux. See{" "}
|
||||||
|
<a href="/docs/platforms#windows">Platforms</a>.
|
||||||
|
</>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
Install these before running AO:
|
Install these before running AO:
|
||||||
|
|
||||||
| Tool | Required | Why AO needs it |
|
| Tool | Required | Why AO needs it |
|
||||||
| --- | --- | --- |
|
| --------------- | ---------------------- | ------------------------------------------------------------------------------------ |
|
||||||
| Node.js 20+ | Yes | Runs the AO CLI, dashboard, and plugins. |
|
| Node.js 20+ | Yes | Runs the AO CLI, dashboard, and plugins. |
|
||||||
| Git | Yes | Creates worktrees, branches, commits, and cleanup operations. |
|
| Git | Yes | Creates worktrees, branches, commits, and cleanup operations. |
|
||||||
| GitHub CLI `gh` | For GitHub projects | Reads issues, PRs, reviews, and CI status as your user. |
|
| GitHub CLI `gh` | For GitHub projects | Reads issues, PRs, reviews, and CI status as your user. |
|
||||||
|
|
@ -38,29 +44,12 @@ If you plan to use GitLab or Linear, install and authenticate their CLIs or cred
|
||||||
## Install AO
|
## Install AO
|
||||||
|
|
||||||
<Tabs items={["npm", "pnpm", "yarn", "from source"]}>
|
<Tabs items={["npm", "pnpm", "yarn", "from source"]}>
|
||||||
<Tab value="npm">
|
<Tab value="npm">```bash npm install -g @aoagents/ao ```</Tab>
|
||||||
```bash
|
<Tab value="pnpm">```bash pnpm add -g @aoagents/ao ```</Tab>
|
||||||
npm install -g @aoagents/ao
|
<Tab value="yarn">```bash yarn global add @aoagents/ao ```</Tab>
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
<Tab value="pnpm">
|
|
||||||
```bash
|
|
||||||
pnpm add -g @aoagents/ao
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
<Tab value="yarn">
|
|
||||||
```bash
|
|
||||||
yarn global add @aoagents/ao
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
<Tab value="from source">
|
<Tab value="from source">
|
||||||
```bash
|
```bash git clone https://github.com/ComposioHQ/agent-orchestrator cd agent-orchestrator pnpm install pnpm build
|
||||||
git clone https://github.com/ComposioHQ/agent-orchestrator
|
pnpm --filter @aoagents/ao link --global ```
|
||||||
cd agent-orchestrator
|
|
||||||
pnpm install
|
|
||||||
pnpm build
|
|
||||||
pnpm --filter @aoagents/ao link --global
|
|
||||||
```
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
|
@ -94,37 +83,11 @@ The authenticated account must be able to read the repository, create branches,
|
||||||
AO launches an agent CLI inside each worker session. Start with the one you already use, then add more later if you want per-project or per-role choices.
|
AO launches an agent CLI inside each worker session. Start with the one you already use, then add more later if you want per-project or per-role choices.
|
||||||
|
|
||||||
<Tabs items={["Claude Code", "Codex", "Cursor", "Aider", "OpenCode"]}>
|
<Tabs items={["Claude Code", "Codex", "Cursor", "Aider", "OpenCode"]}>
|
||||||
<Tab value="Claude Code">
|
<Tab value="Claude Code">```bash npm install -g @anthropic-ai/claude-code claude ```</Tab>
|
||||||
```bash
|
<Tab value="Codex">```bash npm install -g @openai/codex codex ```</Tab>
|
||||||
npm install -g @anthropic-ai/claude-code
|
<Tab value="Cursor">```bash curl https://cursor.com/install -fsS | bash agent --help ```</Tab>
|
||||||
claude
|
<Tab value="Aider">```bash pip install aider-install aider-install aider --help ```</Tab>
|
||||||
```
|
<Tab value="OpenCode">```bash npm install -g opencode-ai opencode --help ```</Tab>
|
||||||
</Tab>
|
|
||||||
<Tab value="Codex">
|
|
||||||
```bash
|
|
||||||
npm install -g @openai/codex
|
|
||||||
codex
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
<Tab value="Cursor">
|
|
||||||
```bash
|
|
||||||
curl https://cursor.com/install -fsS | bash
|
|
||||||
agent --help
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
<Tab value="Aider">
|
|
||||||
```bash
|
|
||||||
pip install aider-install
|
|
||||||
aider-install
|
|
||||||
aider --help
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
<Tab value="OpenCode">
|
|
||||||
```bash
|
|
||||||
npm install -g opencode-ai
|
|
||||||
opencode --help
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
@ -181,10 +144,12 @@ Use Slack, Discord, webhook, or another network notifier for alerts. Desktop not
|
||||||
|
|
||||||
<Accordions>
|
<Accordions>
|
||||||
<Accordion title="`ao` is not found after install">
|
<Accordion title="`ao` is not found after install">
|
||||||
Your global package bin directory is not on `PATH`. Check your package manager's global bin path, then reopen your shell and run `ao --version` again.
|
Your global package bin directory is not on `PATH`. Check your package manager's global bin path, then reopen your
|
||||||
|
shell and run `ao --version` again.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="`gh` is not authenticated">
|
<Accordion title="`gh` is not authenticated">
|
||||||
Run `gh auth login`, then `gh auth status`. AO cannot read GitHub issues, PRs, reviews, or CI checks until `gh` is authenticated.
|
Run `gh auth login`, then `gh auth status`. AO cannot read GitHub issues, PRs, reviews, or CI checks until `gh` is
|
||||||
|
authenticated.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="tmux is missing">
|
<Accordion title="tmux is missing">
|
||||||
Install tmux on macOS or Linux, or set `defaults.runtime: process` if you are on Windows or running in a container.
|
Install tmux on macOS or Linux, or set `defaults.runtime: process` if you are on Windows or running in a container.
|
||||||
|
|
@ -204,6 +169,10 @@ Use Slack, Discord, webhook, or another network notifier for alerts. Desktop not
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Quickstart" description="Run one worker session from start to pull request." href="/docs/quickstart" />
|
<Card title="Quickstart" description="Run one worker session from start to pull request." href="/docs/quickstart" />
|
||||||
<Card title="Configuration" description="Understand the generated `agent-orchestrator.yaml`." href="/docs/configuration" />
|
<Card
|
||||||
|
title="Configuration"
|
||||||
|
description="Understand the generated `agent-orchestrator.yaml`."
|
||||||
|
href="/docs/configuration"
|
||||||
|
/>
|
||||||
<Card title="Platforms" description="Choose the right runtime and notifier for your OS." href="/docs/platforms" />
|
<Card title="Platforms" description="Choose the right runtime and notifier for your OS." href="/docs/platforms" />
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ The platform differences come from the tools used to run and attach to long-live
|
||||||
## Recommended Setup
|
## Recommended Setup
|
||||||
|
|
||||||
| Platform | Runtime | Notifications | Notes |
|
| Platform | Runtime | Notifications | Notes |
|
||||||
| --- | --- | --- | --- |
|
| ---------------------- | ------------------- | ---------------------------------- | -------------------------------------------------------------------------------- |
|
||||||
| macOS | `tmux` | `desktop`, Slack, Discord, webhook | Best local experience. iTerm2 attach support is macOS-only. |
|
| macOS | `tmux` | `desktop`, Slack, Discord, webhook | Best local experience. iTerm2 attach support is macOS-only. |
|
||||||
| Linux | `tmux` | `desktop`, Slack, Discord, webhook | Best server and workstation setup. Desktop notifications need `notify-send`. |
|
| Linux | `tmux` | `desktop`, Slack, Discord, webhook | Best server and workstation setup. Desktop notifications need `notify-send`. |
|
||||||
| Windows | `process` | Slack, Discord, webhook | Native tmux and iTerm2 are unavailable. Windows support is in progress. |
|
| Windows | `process` | Slack, Discord, webhook | Native tmux and iTerm2 are unavailable. Windows support is in progress. |
|
||||||
|
|
@ -47,7 +47,7 @@ defaults:
|
||||||
What works well on macOS:
|
What works well on macOS:
|
||||||
|
|
||||||
| Capability | Status |
|
| Capability | Status |
|
||||||
| --- | --- |
|
| ----------------------------------------- | --------- |
|
||||||
| tmux-backed worker sessions | Supported |
|
| tmux-backed worker sessions | Supported |
|
||||||
| Browser dashboard terminal | Supported |
|
| Browser dashboard terminal | Supported |
|
||||||
| iTerm2 attach/open helpers | Supported |
|
| iTerm2 attach/open helpers | Supported |
|
||||||
|
|
@ -55,7 +55,8 @@ What works well on macOS:
|
||||||
| macOS idle sleep prevention while AO runs | Supported |
|
| macOS idle sleep prevention while AO runs | Supported |
|
||||||
|
|
||||||
<Callout type="info" title="Lid-close sleep still wins">
|
<Callout type="info" title="Lid-close sleep still wins">
|
||||||
AO can prevent idle sleep on macOS while agents run, but it cannot override hardware lid-close sleep. Use normal clamshell mode if you need the machine available while closed.
|
AO can prevent idle sleep on macOS while agents run, but it cannot override hardware lid-close sleep. Use normal
|
||||||
|
clamshell mode if you need the machine available while closed.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
@ -80,7 +81,7 @@ defaults:
|
||||||
What to know:
|
What to know:
|
||||||
|
|
||||||
| Capability | Status |
|
| Capability | Status |
|
||||||
| --- | --- |
|
| --------------------------- | ----------------------------------------------------------- |
|
||||||
| tmux-backed worker sessions | Supported |
|
| tmux-backed worker sessions | Supported |
|
||||||
| Browser dashboard terminal | Supported |
|
| Browser dashboard terminal | Supported |
|
||||||
| iTerm2 attach/open helpers | Not available |
|
| iTerm2 attach/open helpers | Not available |
|
||||||
|
|
@ -92,7 +93,8 @@ If you are running AO on a headless Linux host, prefer Slack, Discord, or webhoo
|
||||||
## Windows
|
## Windows
|
||||||
|
|
||||||
<Callout type="warn" title="Windows support is in progress">
|
<Callout type="warn" title="Windows support is in progress">
|
||||||
The native Windows path uses `runtime: process`. The core spawn and PR workflow is available, but tmux-specific workflows and iTerm2 helpers do not apply.
|
The native Windows path uses `runtime: process`. The core spawn and PR workflow is available, but tmux-specific
|
||||||
|
workflows and iTerm2 helpers do not apply.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
Recommended config:
|
Recommended config:
|
||||||
|
|
@ -107,7 +109,7 @@ defaults:
|
||||||
What works:
|
What works:
|
||||||
|
|
||||||
| Capability | Status |
|
| Capability | Status |
|
||||||
| --- | --- |
|
| --------------------------------------------------------- | ------------------------------------------------------- |
|
||||||
| `ao start`, `ao stop`, `ao dashboard` | Supported |
|
| `ao start`, `ao stop`, `ao dashboard` | Supported |
|
||||||
| `ao spawn` and `ao spawn --prompt` | Supported through the process runtime |
|
| `ao spawn` and `ao spawn --prompt` | Supported through the process runtime |
|
||||||
| GitHub, GitLab, and Linear tracker/SCM integrations | Supported when their CLIs or credentials are configured |
|
| GitHub, GitLab, and Linear tracker/SCM integrations | Supported when their CLIs or credentials are configured |
|
||||||
|
|
@ -117,7 +119,7 @@ What works:
|
||||||
What is limited:
|
What is limited:
|
||||||
|
|
||||||
| Capability | Status | Use instead |
|
| Capability | Status | Use instead |
|
||||||
| --- | --- | --- |
|
| --------------------------- | ---------------------- | ---------------------------------------------- |
|
||||||
| `runtime: tmux` | Not available natively | `runtime: process` |
|
| `runtime: tmux` | Not available natively | `runtime: process` |
|
||||||
| iTerm2 terminal integration | Not available | Browser dashboard terminal |
|
| iTerm2 terminal integration | Not available | Browser dashboard terminal |
|
||||||
| Desktop notifier | No-op on Windows | Slack, Discord, webhook, Composio, or OpenClaw |
|
| Desktop notifier | No-op on Windows | Slack, Discord, webhook, Composio, or OpenClaw |
|
||||||
|
|
@ -148,7 +150,7 @@ Operational notes:
|
||||||
## Choosing A Runtime
|
## Choosing A Runtime
|
||||||
|
|
||||||
| Runtime | Best for | Tradeoff |
|
| Runtime | Best for | Tradeoff |
|
||||||
| --- | --- | --- |
|
| --------- | ------------------------------------------------------------------------- | --------------------------------------------------- |
|
||||||
| `tmux` | macOS/Linux machines where you want durable, attachable terminal sessions | Requires tmux and does not work natively on Windows |
|
| `tmux` | macOS/Linux machines where you want durable, attachable terminal sessions | Requires tmux and does not work natively on Windows |
|
||||||
| `process` | Windows, containers, and simpler process-managed environments | Less of the workflow is tmux-attachable |
|
| `process` | Windows, containers, and simpler process-managed environments | Less of the workflow is tmux-attachable |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ import { Accordions, Accordion } from "fumadocs-ui/components/accordion";
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="aider" size={28} />
|
<Logo name="aider" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>agent</code> · Name: <code>aider</code> · Binary: <code>aider</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>agent</code> · Name: <code>aider</code> · Binary: <code>aider</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
[Aider](https://aider.chat) is a pair-programming CLI built around explicit file edits. It doesn't have a session-resume concept, but it does work well for small, focused issues.
|
[Aider](https://aider.chat) is a pair-programming CLI built around explicit file edits. It doesn't have a session-resume concept, but it does work well for small, focused issues.
|
||||||
|
|
@ -38,7 +40,7 @@ agent: aider
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
| Variable | Set by AO | Purpose |
|
| Variable | Set by AO | Purpose |
|
||||||
|---|---|---|
|
| --------------- | --------- | -------------------------- |
|
||||||
| `AO_SESSION_ID` | ✓ | AO session id |
|
| `AO_SESSION_ID` | ✓ | AO session id |
|
||||||
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
||||||
| `PATH` | ✓ | Prepends `~/.ao/bin` |
|
| `PATH` | ✓ | Prepends `~/.ao/bin` |
|
||||||
|
|
@ -48,7 +50,8 @@ agent: aider
|
||||||
|
|
||||||
<Accordions>
|
<Accordions>
|
||||||
<Accordion title="Aider hangs on file-edit confirmation">
|
<Accordion title="Aider hangs on file-edit confirmation">
|
||||||
Aider prompts before applying diffs. If you want fully autonomous runs, configure Aider's `--yes-always` via `~/.aider.conf.yml`. AO won't inject this for you — it's your call whether the agent should auto-apply.
|
Aider prompts before applying diffs. If you want fully autonomous runs, configure Aider's `--yes-always` via
|
||||||
|
`~/.aider.conf.yml`. AO won't inject this for you — it's your call whether the agent should auto-apply.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Dashboard cost shows $0">
|
<Accordion title="Dashboard cost shows $0">
|
||||||
AO doesn't parse Aider's cost output. The cost column stays empty.
|
AO doesn't parse Aider's cost output. The cost column stays empty.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ import { Callout } from "fumadocs-ui/components/callout";
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="claude-code" size={28} />
|
<Logo name="claude-code" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>agent</code> · Name: <code>claude-code</code> · Binary: <code>claude</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>agent</code> · Name: <code>claude-code</code> · Binary: <code>claude</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
[Claude Code](https://docs.anthropic.com/en/docs/claude-code) is Anthropic's CLI coding agent. It's AO's default because it has first-class session resume, rich JSONL event logs, and a native hook system AO can register against.
|
[Claude Code](https://docs.anthropic.com/en/docs/claude-code) is Anthropic's CLI coding agent. It's AO's default because it has first-class session resume, rich JSONL event logs, and a native hook system AO can register against.
|
||||||
|
|
@ -44,7 +46,7 @@ That's it — there are no plugin-level config keys.
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
| Variable | Set by AO | Purpose |
|
| Variable | Set by AO | Purpose |
|
||||||
|---|---|---|
|
| --------------- | ------------------------------ | --------------------------------------------------- |
|
||||||
| `AO_SESSION_ID` | ✓ | Unique AO session identifier |
|
| `AO_SESSION_ID` | ✓ | Unique AO session identifier |
|
||||||
| `AO_ISSUE_ID` | ✓ (when spawned from an issue) | Issue identifier |
|
| `AO_ISSUE_ID` | ✓ (when spawned from an issue) | Issue identifier |
|
||||||
| `CLAUDECODE` | ✓ | Signals to Claude that it's running under a harness |
|
| `CLAUDECODE` | ✓ | Signals to Claude that it's running under a harness |
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ import { Accordions, Accordion } from "fumadocs-ui/components/accordion";
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="codex" size={28} />
|
<Logo name="codex" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>agent</code> · Name: <code>codex</code> · Binary: <code>codex</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>agent</code> · Name: <code>codex</code> · Binary: <code>codex</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
[OpenAI Codex CLI](https://github.com/openai/codex) is a terminal coding agent from OpenAI. AO hooks into it via PATH wrappers and reads its native session JSONL for activity + cost.
|
[OpenAI Codex CLI](https://github.com/openai/codex) is a terminal coding agent from OpenAI. AO hooks into it via PATH wrappers and reads its native session JSONL for activity + cost.
|
||||||
|
|
@ -38,7 +40,7 @@ No plugin-level config.
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
| Variable | Set by AO | Purpose |
|
| Variable | Set by AO | Purpose |
|
||||||
|---|---|---|
|
| ---------------------------- | --------- | --------------------------------------------------- |
|
||||||
| `AO_SESSION_ID` | ✓ | AO session id |
|
| `AO_SESSION_ID` | ✓ | AO session id |
|
||||||
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
||||||
| `PATH` | ✓ | Prepends `~/.ao/bin` for the wrappers |
|
| `PATH` | ✓ | Prepends `~/.ao/bin` for the wrappers |
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ import { Accordions, Accordion } from "fumadocs-ui/components/accordion";
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="cursor" size={28} />
|
<Logo name="cursor" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>agent</code> · Name: <code>cursor</code> · Binary: <code>agent</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>agent</code> · Name: <code>cursor</code> · Binary: <code>agent</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The [Cursor Agent CLI](https://cursor.com/docs) brings Cursor's editing model to a terminal. AO spawns it per session, wraps `gh`/`git`, and tracks activity via the AO activity log.
|
The [Cursor Agent CLI](https://cursor.com/docs) brings Cursor's editing model to a terminal. AO spawns it per session, wraps `gh`/`git`, and tracks activity via the AO activity log.
|
||||||
|
|
@ -50,7 +52,7 @@ The plugin's `detect()` checks `agent --help` output for the strings `Cursor Age
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
| Variable | Set by AO | Purpose |
|
| Variable | Set by AO | Purpose |
|
||||||
|---|---|---|
|
| --------------- | --------- | -------------------------- |
|
||||||
| `AO_SESSION_ID` | ✓ | AO session id |
|
| `AO_SESSION_ID` | ✓ | AO session id |
|
||||||
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
||||||
| `PATH` | ✓ | Prepends `~/.ao/bin` |
|
| `PATH` | ✓ | Prepends `~/.ao/bin` |
|
||||||
|
|
@ -60,9 +62,11 @@ The plugin's `detect()` checks `agent --help` output for the strings `Cursor Age
|
||||||
|
|
||||||
<Accordions>
|
<Accordions>
|
||||||
<Accordion title="AO says Cursor Agent isn't detected">
|
<Accordion title="AO says Cursor Agent isn't detected">
|
||||||
The plugin only recognises the real Cursor binary by its help output. Run `agent --help` — if you see a different tool's help, remove or shadow it, or reinstall Cursor Agent.
|
The plugin only recognises the real Cursor binary by its help output. Run `agent --help` — if you see a different
|
||||||
|
tool's help, remove or shadow it, or reinstall Cursor Agent.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Dashboard never leaves `active`">
|
<Accordion title="Dashboard never leaves `active`">
|
||||||
Cursor doesn't emit its own session JSONL. AO classifies based on terminal output — if your terminal is being captured by another tool, the classification can drift. `ao doctor` will flag this.
|
Cursor doesn't emit its own session JSONL. AO classifies based on terminal output — if your terminal is being
|
||||||
|
captured by another tool, the classification can drift. `ao doctor` will flag this.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Accordions>
|
</Accordions>
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,17 @@ description: Pick the agent that writes the code. AO supports five today, and th
|
||||||
The agent is the piece that actually writes code. Everything else in AO — worktrees, runtimes, notifiers — is plumbing around it.
|
The agent is the piece that actually writes code. Everything else in AO — worktrees, runtimes, notifiers — is plumbing around it.
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
**PR metadata is captured automatically.** Non-Claude-Code agents (Codex, Cursor, Aider, OpenCode) use `~/.ao/bin/gh` and `~/.ao/bin/git` PATH wrappers that intercept commands like `gh pr create` and record the resulting PR number and branch so the dashboard stays in sync. Claude Code uses PostToolUse hooks in `.claude/settings.json` instead — the same metadata is written through Claude Code's native hook system. Both approaches are set up transparently by AO; you don't need to configure anything.
|
**PR metadata is captured automatically.** Non-Claude-Code agents (Codex, Cursor, Aider, OpenCode) use `~/.ao/bin/gh`
|
||||||
|
and `~/.ao/bin/git` PATH wrappers that intercept commands like `gh pr create` and record the resulting PR number and
|
||||||
|
branch so the dashboard stays in sync. Claude Code uses PostToolUse hooks in `.claude/settings.json` instead — the
|
||||||
|
same metadata is written through Claude Code's native hook system. Both approaches are set up transparently by AO; you
|
||||||
|
don't need to configure anything.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Supported agents
|
## Supported agents
|
||||||
|
|
||||||
| Agent | Slot name | Binary | Session resume |
|
| Agent | Slot name | Binary | Session resume |
|
||||||
|---|---|---|---|
|
| ----------------------------------------------- | ------------- | ---------- | ---------------------------- |
|
||||||
| [Claude Code](/docs/plugins/agents/claude-code) | `claude-code` | `claude` | ✅ `--resume` |
|
| [Claude Code](/docs/plugins/agents/claude-code) | `claude-code` | `claude` | ✅ `--resume` |
|
||||||
| [Codex](/docs/plugins/agents/codex) | `codex` | `codex` | ✅ `codex resume <threadId>` |
|
| [Codex](/docs/plugins/agents/codex) | `codex` | `codex` | ✅ `codex resume <threadId>` |
|
||||||
| [Cursor](/docs/plugins/agents/cursor) | `cursor` | `agent` | ❌ |
|
| [Cursor](/docs/plugins/agents/cursor) | `cursor` | `agent` | ❌ |
|
||||||
|
|
@ -20,11 +24,21 @@ The agent is the piece that actually writes code. Everything else in AO — work
|
||||||
| [OpenCode](/docs/plugins/agents/opencode) | `opencode` | `opencode` | ✅ via OpenCode session API |
|
| [OpenCode](/docs/plugins/agents/opencode) | `opencode` | `opencode` | ✅ via OpenCode session API |
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="Claude Code" logo="claude-code" href="/docs/plugins/agents/claude-code" description="Anthropic's CLI coding agent." />
|
<PluginCard
|
||||||
|
name="Claude Code"
|
||||||
|
logo="claude-code"
|
||||||
|
href="/docs/plugins/agents/claude-code"
|
||||||
|
description="Anthropic's CLI coding agent."
|
||||||
|
/>
|
||||||
<PluginCard name="Codex" logo="codex" href="/docs/plugins/agents/codex" description="OpenAI's Codex CLI." />
|
<PluginCard name="Codex" logo="codex" href="/docs/plugins/agents/codex" description="OpenAI's Codex CLI." />
|
||||||
<PluginCard name="Cursor" logo="cursor" href="/docs/plugins/agents/cursor" description="Cursor Agent CLI." />
|
<PluginCard name="Cursor" logo="cursor" href="/docs/plugins/agents/cursor" description="Cursor Agent CLI." />
|
||||||
<PluginCard name="Aider" logo="aider" href="/docs/plugins/agents/aider" description="Aider pair-programming CLI." />
|
<PluginCard name="Aider" logo="aider" href="/docs/plugins/agents/aider" description="Aider pair-programming CLI." />
|
||||||
<PluginCard name="OpenCode" logo="opencode" href="/docs/plugins/agents/opencode" description="OpenCode terminal agent." />
|
<PluginCard
|
||||||
|
name="OpenCode"
|
||||||
|
logo="opencode"
|
||||||
|
href="/docs/plugins/agents/opencode"
|
||||||
|
description="OpenCode terminal agent."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Choosing
|
## Choosing
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
{
|
{
|
||||||
"title": "Agents",
|
"title": "Agents",
|
||||||
"pages": [
|
"pages": ["claude-code", "codex", "cursor", "aider", "opencode"]
|
||||||
"claude-code",
|
|
||||||
"codex",
|
|
||||||
"cursor",
|
|
||||||
"aider",
|
|
||||||
"opencode"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ import { Accordions, Accordion } from "fumadocs-ui/components/accordion";
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="opencode" size={28} />
|
<Logo name="opencode" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>agent</code> · Name: <code>opencode</code> · Binary: <code>opencode</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>agent</code> · Name: <code>opencode</code> · Binary: <code>opencode</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
[OpenCode](https://opencode.ai) is an open-source terminal coding agent. It has a structured session API, which means AO can discover, resume, and track its sessions reliably.
|
[OpenCode](https://opencode.ai) is an open-source terminal coding agent. It has a structured session API, which means AO can discover, resume, and track its sessions reliably.
|
||||||
|
|
@ -39,7 +41,7 @@ No plugin-level config.
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
| Variable | Set by AO | Purpose |
|
| Variable | Set by AO | Purpose |
|
||||||
|---|---|---|
|
| --------------- | --------- | -------------------------- |
|
||||||
| `AO_SESSION_ID` | ✓ | AO session id |
|
| `AO_SESSION_ID` | ✓ | AO session id |
|
||||||
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
||||||
| `PATH` | ✓ | Prepends `~/.ao/bin` |
|
| `PATH` | ✓ | Prepends `~/.ao/bin` |
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ Add `--non-interactive` to require all fields to be provided via flags (useful i
|
||||||
The CLI asks for four things, in order:
|
The CLI asks for four things, in order:
|
||||||
|
|
||||||
| Prompt | Example input |
|
| Prompt | Example input |
|
||||||
|--------|--------------|
|
| ------------------- | ------------------------------------------------------------ |
|
||||||
| Plugin display name | `PagerDuty` |
|
| Plugin display name | `PagerDuty` |
|
||||||
| Plugin slot | `notifier` (selected from a list of all 7 slots) |
|
| Plugin slot | `notifier` (selected from a list of all 7 slots) |
|
||||||
| Short description | `Route urgent alerts to PagerDuty` |
|
| Short description | `Route urgent alerts to PagerDuty` |
|
||||||
|
|
@ -227,7 +227,7 @@ All interfaces are defined in `packages/core/src/types.ts` and exported from `@a
|
||||||
### Runtime
|
### Runtime
|
||||||
|
|
||||||
| Method | Signature | Required |
|
| Method | Signature | Required |
|
||||||
|--------|-----------|---------|
|
| --------------- | ------------------------------------------------------------ | -------- |
|
||||||
| `create` | `(config: RuntimeCreateConfig) => Promise<RuntimeHandle>` | yes |
|
| `create` | `(config: RuntimeCreateConfig) => Promise<RuntimeHandle>` | yes |
|
||||||
| `destroy` | `(handle: RuntimeHandle) => Promise<void>` | yes |
|
| `destroy` | `(handle: RuntimeHandle) => Promise<void>` | yes |
|
||||||
| `sendMessage` | `(handle: RuntimeHandle, message: string) => Promise<void>` | yes |
|
| `sendMessage` | `(handle: RuntimeHandle, message: string) => Promise<void>` | yes |
|
||||||
|
|
@ -243,7 +243,7 @@ Agent plugins have the richest interface. Methods are split into required and op
|
||||||
**Required:**
|
**Required:**
|
||||||
|
|
||||||
| Method | Purpose | Returns `null`? |
|
| Method | Purpose | Returns `null`? |
|
||||||
|--------|---------|----------------|
|
| ------------------ | ----------------------------------------------------------------- | -------------------- |
|
||||||
| `getLaunchCommand` | Shell command to start the agent | No |
|
| `getLaunchCommand` | Shell command to start the agent | No |
|
||||||
| `getEnvironment` | Env vars for the process (must include `~/.ao/bin` in PATH) | No |
|
| `getEnvironment` | Env vars for the process (must include `~/.ao/bin` in PATH) | No |
|
||||||
| `detectActivity` | Terminal-output activity classification (deprecated but required) | No |
|
| `detectActivity` | Terminal-output activity classification (deprecated but required) | No |
|
||||||
|
|
@ -254,20 +254,22 @@ Agent plugins have the richest interface. Methods are split into required and op
|
||||||
**Optional:**
|
**Optional:**
|
||||||
|
|
||||||
| Method | Purpose | When to skip |
|
| Method | Purpose | When to skip |
|
||||||
|--------|---------|-------------|
|
| --------------------- | ---------------------------------------- | ------------------------------------- |
|
||||||
| `getRestoreCommand` | Resume a previous session | Agent has no resume capability |
|
| `getRestoreCommand` | Resume a previous session | Agent has no resume capability |
|
||||||
| `setupWorkspaceHooks` | Install metadata hooks for PR tracking | Never — required for the dashboard |
|
| `setupWorkspaceHooks` | Install metadata hooks for PR tracking | Never — required for the dashboard |
|
||||||
| `postLaunchSetup` | Post-launch config | Only if no post-launch work is needed |
|
| `postLaunchSetup` | Post-launch config | Only if no post-launch work is needed |
|
||||||
| `recordActivity` | Write terminal-derived activity to JSONL | Agent has native JSONL (Claude Code) |
|
| `recordActivity` | Write terminal-derived activity to JSONL | Agent has native JSONL (Claude Code) |
|
||||||
|
|
||||||
<Callout type="warn">
|
<Callout type="warn">
|
||||||
`setupWorkspaceHooks` is marked optional in the TypeScript interface but is critical in practice. Without it, PRs created by your agent will never appear in the dashboard. See [Agent plugin specifics](#agent-plugin-specifics) for the two patterns (agent-native hooks vs PATH wrappers).
|
`setupWorkspaceHooks` is marked optional in the TypeScript interface but is critical in practice. Without it, PRs
|
||||||
|
created by your agent will never appear in the dashboard. See [Agent plugin specifics](#agent-plugin-specifics) for
|
||||||
|
the two patterns (agent-native hooks vs PATH wrappers).
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
### Workspace
|
### Workspace
|
||||||
|
|
||||||
| Method | Signature | Required |
|
| Method | Signature | Required |
|
||||||
|--------|-----------|---------|
|
| ------------ | ---------------------------------------------------------------------------------- | -------- |
|
||||||
| `create` | `(config: WorkspaceCreateConfig) => Promise<WorkspaceInfo>` | yes |
|
| `create` | `(config: WorkspaceCreateConfig) => Promise<WorkspaceInfo>` | yes |
|
||||||
| `destroy` | `(workspacePath: string) => Promise<void>` | yes |
|
| `destroy` | `(workspacePath: string) => Promise<void>` | yes |
|
||||||
| `list` | `(projectId: string) => Promise<WorkspaceInfo[]>` | yes |
|
| `list` | `(projectId: string) => Promise<WorkspaceInfo[]>` | yes |
|
||||||
|
|
@ -278,7 +280,7 @@ Agent plugins have the richest interface. Methods are split into required and op
|
||||||
### Tracker
|
### Tracker
|
||||||
|
|
||||||
| Method | Signature | Required |
|
| Method | Signature | Required |
|
||||||
|--------|-----------|---------|
|
| ---------------- | ------------------------------------------------------------------------------------ | -------- |
|
||||||
| `getIssue` | `(identifier: string, project: ProjectConfig) => Promise<Issue>` | yes |
|
| `getIssue` | `(identifier: string, project: ProjectConfig) => Promise<Issue>` | yes |
|
||||||
| `isCompleted` | `(identifier: string, project: ProjectConfig) => Promise<boolean>` | yes |
|
| `isCompleted` | `(identifier: string, project: ProjectConfig) => Promise<boolean>` | yes |
|
||||||
| `issueUrl` | `(identifier: string, project: ProjectConfig) => string` | yes |
|
| `issueUrl` | `(identifier: string, project: ProjectConfig) => string` | yes |
|
||||||
|
|
@ -300,7 +302,7 @@ The richest interface — covers PR lifecycle, CI tracking, review tracking, and
|
||||||
### Notifier
|
### Notifier
|
||||||
|
|
||||||
| Method | Signature | Required |
|
| Method | Signature | Required |
|
||||||
|--------|-----------|---------|
|
| ------------------- | ----------------------------------------------------------------------- | -------- |
|
||||||
| `notify` | `(event: OrchestratorEvent) => Promise<void>` | yes |
|
| `notify` | `(event: OrchestratorEvent) => Promise<void>` | yes |
|
||||||
| `notifyWithActions` | `(event: OrchestratorEvent, actions: NotifyAction[]) => Promise<void>` | optional |
|
| `notifyWithActions` | `(event: OrchestratorEvent, actions: NotifyAction[]) => Promise<void>` | optional |
|
||||||
| `post` | `(message: string, context?: NotifyContext) => Promise<string \| null>` | optional |
|
| `post` | `(message: string, context?: NotifyContext) => Promise<string \| null>` | optional |
|
||||||
|
|
@ -308,7 +310,7 @@ The richest interface — covers PR lifecycle, CI tracking, review tracking, and
|
||||||
### Terminal
|
### Terminal
|
||||||
|
|
||||||
| Method | Signature | Required |
|
| Method | Signature | Required |
|
||||||
|--------|-----------|---------|
|
| --------------- | ---------------------------------------- | -------- |
|
||||||
| `openSession` | `(session: Session) => Promise<void>` | yes |
|
| `openSession` | `(session: Session) => Promise<void>` | yes |
|
||||||
| `openAll` | `(sessions: Session[]) => Promise<void>` | yes |
|
| `openAll` | `(sessions: Session[]) => Promise<void>` | yes |
|
||||||
| `isSessionOpen` | `(session: Session) => Promise<boolean>` | optional |
|
| `isSessionOpen` | `(session: Session) => Promise<boolean>` | optional |
|
||||||
|
|
@ -421,7 +423,7 @@ projects:
|
||||||
## `ao plugin` subcommands
|
## `ao plugin` subcommands
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|---------|-------------|
|
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `ao plugin list` | List plugins from the bundled marketplace catalog. Add `--installed` to show only what's in your config. Filter by slot with `--type <slot>`. Add `--refresh` to pull the latest catalog from the registry. |
|
| `ao plugin list` | List plugins from the bundled marketplace catalog. Add `--installed` to show only what's in your config. Filter by slot with `--type <slot>`. Add `--refresh` to pull the latest catalog from the registry. |
|
||||||
| `ao plugin search <query>` | Search the bundled catalog by name, package, description, or slot. |
|
| `ao plugin search <query>` | Search the bundled catalog by name, package, description, or slot. |
|
||||||
| `ao plugin create [dir]` | Scaffold a new plugin package interactively. |
|
| `ao plugin create [dir]` | Scaffold a new plugin package interactively. |
|
||||||
|
|
@ -438,14 +440,14 @@ All utilities are exported from `@aoagents/ao-core`. The source lives in `packag
|
||||||
**Shell safety and HTTP:**
|
**Shell safety and HTTP:**
|
||||||
|
|
||||||
| Export | Purpose |
|
| Export | Purpose |
|
||||||
|--------|---------|
|
| ------------- | --------------------------------------------------------------------------------------- |
|
||||||
| `shellEscape` | Safely escape command-line arguments. Use for every argument passed to child processes. |
|
| `shellEscape` | Safely escape command-line arguments. Use for every argument passed to child processes. |
|
||||||
| `validateUrl` | Validate a webhook URL and throw a descriptive error on failure. |
|
| `validateUrl` | Validate a webhook URL and throw a descriptive error on failure. |
|
||||||
|
|
||||||
**Activity detection (agent plugins):**
|
**Activity detection (agent plugins):**
|
||||||
|
|
||||||
| Export | Purpose |
|
| Export | Purpose |
|
||||||
|--------|---------|
|
| -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `readLastJsonlEntry` | Efficiently read the last entry from an agent's native JSONL log. |
|
| `readLastJsonlEntry` | Efficiently read the last entry from an agent's native JSONL log. |
|
||||||
| `readLastActivityEntry` | Read the last entry from the AO activity JSONL (`{workspace}/.ao/activity.jsonl`). |
|
| `readLastActivityEntry` | Read the last entry from the AO activity JSONL (`{workspace}/.ao/activity.jsonl`). |
|
||||||
| `checkActivityLogState` | Extract `waiting_input` or `blocked` from an activity entry (with staleness cap). Returns `null` for other states. |
|
| `checkActivityLogState` | Extract `waiting_input` or `blocked` from an activity entry (with staleness cap). Returns `null` for other states. |
|
||||||
|
|
@ -457,7 +459,7 @@ All utilities are exported from `@aoagents/ao-core`. The source lives in `packag
|
||||||
**Workspace setup (agent plugins):**
|
**Workspace setup (agent plugins):**
|
||||||
|
|
||||||
| Export | Purpose |
|
| Export | Purpose |
|
||||||
|--------|---------|
|
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `setupPathWrapperWorkspace` | Install `~/.ao/bin/gh` and `~/.ao/bin/git` PATH wrappers and write `.ao/AGENTS.md` in the workspace. Required for PATH-wrapper agents (Codex, Aider, OpenCode). |
|
| `setupPathWrapperWorkspace` | Install `~/.ao/bin/gh` and `~/.ao/bin/git` PATH wrappers and write `.ao/AGENTS.md` in the workspace. Required for PATH-wrapper agents (Codex, Aider, OpenCode). |
|
||||||
| `buildAgentPath` | Prepend `~/.ao/bin` to a PATH string. |
|
| `buildAgentPath` | Prepend `~/.ao/bin` to a PATH string. |
|
||||||
| `normalizeAgentPermissionMode` | Normalize legacy permission mode aliases (e.g. `"skip"` → `"permissionless"`). |
|
| `normalizeAgentPermissionMode` | Normalize legacy permission mode aliases (e.g. `"skip"` → `"permissionless"`). |
|
||||||
|
|
@ -465,7 +467,7 @@ All utilities are exported from `@aoagents/ao-core`. The source lives in `packag
|
||||||
**Constants:**
|
**Constants:**
|
||||||
|
|
||||||
| Export | Value |
|
| Export | Value |
|
||||||
|--------|-------|
|
| ----------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||||
| `DEFAULT_READY_THRESHOLD_MS` | `300_000` (5 min) — ready → idle threshold |
|
| `DEFAULT_READY_THRESHOLD_MS` | `300_000` (5 min) — ready → idle threshold |
|
||||||
| `DEFAULT_ACTIVE_WINDOW_MS` | `30_000` (30 s) — active → ready window |
|
| `DEFAULT_ACTIVE_WINDOW_MS` | `30_000` (30 s) — active → ready window |
|
||||||
| `ACTIVITY_INPUT_STALENESS_MS` | Deprecated compatibility export (`300_000`); waiting_input/blocked no longer expire by wallclock |
|
| `ACTIVITY_INPUT_STALENESS_MS` | Deprecated compatibility export (`300_000`); waiting_input/blocked no longer expire by wallclock |
|
||||||
|
|
@ -525,7 +527,7 @@ For agent plugins, the required `getActivityState` tests are listed in the CLAUD
|
||||||
## Common pitfalls
|
## Common pitfalls
|
||||||
|
|
||||||
| Pitfall | Correct approach |
|
| Pitfall | Correct approach |
|
||||||
|---------|-----------------|
|
| ------------------------------------ | ------------------------------------------------------------------ |
|
||||||
| Hardcoded secrets (API keys, tokens) | Read from `process.env`, throw if required and missing |
|
| Hardcoded secrets (API keys, tokens) | Read from `process.env`, throw if required and missing |
|
||||||
| Shell injection | Use `shellEscape()` for every argument passed to child processes |
|
| Shell injection | Use `shellEscape()` for every argument passed to child processes |
|
||||||
| Reading large log files in full | Use `readLastJsonlEntry()` or stream the tail |
|
| Reading large log files in full | Use `readLastJsonlEntry()` or stream the tail |
|
||||||
|
|
@ -539,7 +541,10 @@ For agent plugins, the required `getActivityState` tests are listed in the CLAUD
|
||||||
Agent plugins have significantly more surface area than other slot types. The most critical method is `getActivityState`, which powers the dashboard, stuck-detection, and the lifecycle manager's reaction engine.
|
Agent plugins have significantly more surface area than other slot types. The most critical method is `getActivityState`, which powers the dashboard, stuck-detection, and the lifecycle manager's reaction engine.
|
||||||
|
|
||||||
<Callout type="warn">
|
<Callout type="warn">
|
||||||
**Step 4 of the `getActivityState` cascade (`getActivityFallbackState`) is mandatory.** If you skip it, `getActivityState` returns `null` whenever the native API is unavailable (binary not found, session lookup failed, timeout). The dashboard shows no activity state and stuck-detection stops working entirely. This was a real production bug in the OpenCode plugin.
|
**Step 4 of the `getActivityState` cascade (`getActivityFallbackState`) is mandatory.** If you skip it,
|
||||||
|
`getActivityState` returns `null` whenever the native API is unavailable (binary not found, session lookup failed,
|
||||||
|
timeout). The dashboard shows no activity state and stuck-detection stops working entirely. This was a real production
|
||||||
|
bug in the OpenCode plugin.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
The required 4-step cascade:
|
The required 4-step cascade:
|
||||||
|
|
@ -587,7 +592,15 @@ For agents that don't have a native session API (most new agents), skip step 3 a
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Architecture" description="Understand the plugin registry, session lifecycle, and how slots wire together." href="/docs/architecture" />
|
<Card
|
||||||
|
title="Architecture"
|
||||||
|
description="Understand the plugin registry, session lifecycle, and how slots wire together."
|
||||||
|
href="/docs/architecture"
|
||||||
|
/>
|
||||||
<Card title="Plugin catalog" description="Browse every bundled plugin grouped by slot." href="/docs/plugins" />
|
<Card title="Plugin catalog" description="Browse every bundled plugin grouped by slot." href="/docs/plugins" />
|
||||||
<Card title="Configuration reference" description="How plugins are referenced in agent-orchestrator.yaml." href="/docs/configuration" />
|
<Card
|
||||||
|
title="Configuration reference"
|
||||||
|
description="How plugins are referenced in agent-orchestrator.yaml."
|
||||||
|
href="/docs/configuration"
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ AO has **eight plugin slots**. Only one plugin per slot is active at a time, and
|
||||||
## Slots at a glance
|
## Slots at a glance
|
||||||
|
|
||||||
| Slot | Default | What it does |
|
| Slot | Default | What it does |
|
||||||
|---|---|---|
|
| ------------- | ----------------------------------------- | -------------------------------------------- |
|
||||||
| **Agent** | `claude-code` | Which AI tool writes the code |
|
| **Agent** | `claude-code` | Which AI tool writes the code |
|
||||||
| **Runtime** | `tmux` (macOS/Linux), `process` (Windows) | Where the agent process runs |
|
| **Runtime** | `tmux` (macOS/Linux), `process` (Windows) | Where the agent process runs |
|
||||||
| **Workspace** | `worktree` | Per-session code isolation |
|
| **Workspace** | `worktree` | Per-session code isolation |
|
||||||
|
|
@ -21,59 +21,169 @@ AO has **eight plugin slots**. Only one plugin per slot is active at a time, and
|
||||||
## Agents
|
## Agents
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="Claude Code" logo="claude-code" href="/docs/plugins/agents/claude-code" description="Anthropic's CLI coding agent. Session resume via --resume." />
|
<PluginCard
|
||||||
<PluginCard name="Codex" logo="codex" href="/docs/plugins/agents/codex" description="OpenAI Codex CLI. Session resume via codex resume <threadId>." />
|
name="Claude Code"
|
||||||
<PluginCard name="Cursor" logo="cursor" href="/docs/plugins/agents/cursor" description="Cursor Agent CLI. One-shot per session." />
|
logo="claude-code"
|
||||||
<PluginCard name="Aider" logo="aider" href="/docs/plugins/agents/aider" description="Aider pair-programming CLI. No resume; PATH wrappers for PR tracking." />
|
href="/docs/plugins/agents/claude-code"
|
||||||
<PluginCard name="OpenCode" logo="opencode" href="/docs/plugins/agents/opencode" description="OpenCode terminal agent. Session discovery + restore via the OpenCode session API." />
|
description="Anthropic's CLI coding agent. Session resume via --resume."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Codex"
|
||||||
|
logo="codex"
|
||||||
|
href="/docs/plugins/agents/codex"
|
||||||
|
description="OpenAI Codex CLI. Session resume via codex resume <threadId>."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Cursor"
|
||||||
|
logo="cursor"
|
||||||
|
href="/docs/plugins/agents/cursor"
|
||||||
|
description="Cursor Agent CLI. One-shot per session."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Aider"
|
||||||
|
logo="aider"
|
||||||
|
href="/docs/plugins/agents/aider"
|
||||||
|
description="Aider pair-programming CLI. No resume; PATH wrappers for PR tracking."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="OpenCode"
|
||||||
|
logo="opencode"
|
||||||
|
href="/docs/plugins/agents/opencode"
|
||||||
|
description="OpenCode terminal agent. Session discovery + restore via the OpenCode session API."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Runtimes
|
## Runtimes
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="tmux" logo="tmux" href="/docs/plugins/runtimes/tmux" description="Default on macOS/Linux. Each agent gets its own tmux window you can attach to." />
|
<PluginCard
|
||||||
<PluginCard name="process" logo="windows" href="/docs/plugins/runtimes/process" description="Cross-platform child-process runtime. Required on Windows." />
|
name="tmux"
|
||||||
|
logo="tmux"
|
||||||
|
href="/docs/plugins/runtimes/tmux"
|
||||||
|
description="Default on macOS/Linux. Each agent gets its own tmux window you can attach to."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="process"
|
||||||
|
logo="windows"
|
||||||
|
href="/docs/plugins/runtimes/process"
|
||||||
|
description="Cross-platform child-process runtime. Required on Windows."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Workspaces
|
## Workspaces
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="worktree" logo="github" href="/docs/plugins/workspaces/worktree" description="git worktree per session. Fast, shares the object DB with your main checkout." />
|
<PluginCard
|
||||||
<PluginCard name="clone" logo="github" href="/docs/plugins/workspaces/clone" description="Full clone per session. Use when your tooling doesn't play well with shared .git." />
|
name="worktree"
|
||||||
|
logo="github"
|
||||||
|
href="/docs/plugins/workspaces/worktree"
|
||||||
|
description="git worktree per session. Fast, shares the object DB with your main checkout."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="clone"
|
||||||
|
logo="github"
|
||||||
|
href="/docs/plugins/workspaces/clone"
|
||||||
|
description="Full clone per session. Use when your tooling doesn't play well with shared .git."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Trackers
|
## Trackers
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="GitHub" logo="github" href="/docs/plugins/trackers/github" description="Issues + PRs via the gh CLI." />
|
<PluginCard
|
||||||
<PluginCard name="GitLab" logo="gitlab" href="/docs/plugins/trackers/gitlab" description="Issues + MRs via the glab CLI. Self-hosted supported." />
|
name="GitHub"
|
||||||
<PluginCard name="Linear" logo="linear" href="/docs/plugins/trackers/linear" description="Linear issues. Direct API or Composio-mediated." />
|
logo="github"
|
||||||
|
href="/docs/plugins/trackers/github"
|
||||||
|
description="Issues + PRs via the gh CLI."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="GitLab"
|
||||||
|
logo="gitlab"
|
||||||
|
href="/docs/plugins/trackers/gitlab"
|
||||||
|
description="Issues + MRs via the glab CLI. Self-hosted supported."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Linear"
|
||||||
|
logo="linear"
|
||||||
|
href="/docs/plugins/trackers/linear"
|
||||||
|
description="Linear issues. Direct API or Composio-mediated."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## SCM
|
## SCM
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="GitHub" logo="github" href="/docs/plugins/scm/github" description="PRs, reviews, and CI status via gh." />
|
<PluginCard
|
||||||
<PluginCard name="GitLab" logo="gitlab" href="/docs/plugins/scm/gitlab" description="MRs, discussions, pipelines via glab." />
|
name="GitHub"
|
||||||
|
logo="github"
|
||||||
|
href="/docs/plugins/scm/github"
|
||||||
|
description="PRs, reviews, and CI status via gh."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="GitLab"
|
||||||
|
logo="gitlab"
|
||||||
|
href="/docs/plugins/scm/gitlab"
|
||||||
|
description="MRs, discussions, pipelines via glab."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Notifiers
|
## Notifiers
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="Dashboard" logo="web" href="/docs/plugins/notifiers/dashboard" description="Retained notifications inside the AO dashboard." />
|
<PluginCard
|
||||||
<PluginCard name="Desktop" logo="apple" href="/docs/plugins/notifiers/desktop" description="Native macOS/Linux notifications. No-op on Windows." />
|
name="Dashboard"
|
||||||
<PluginCard name="Discord" logo="discord" href="/docs/plugins/notifiers/discord" description="Webhook-based Discord messages with rich embeds." />
|
logo="web"
|
||||||
|
href="/docs/plugins/notifiers/dashboard"
|
||||||
|
description="Retained notifications inside the AO dashboard."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Desktop"
|
||||||
|
logo="apple"
|
||||||
|
href="/docs/plugins/notifiers/desktop"
|
||||||
|
description="Native macOS/Linux notifications. No-op on Windows."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Discord"
|
||||||
|
logo="discord"
|
||||||
|
href="/docs/plugins/notifiers/discord"
|
||||||
|
description="Webhook-based Discord messages with rich embeds."
|
||||||
|
/>
|
||||||
<PluginCard name="Slack" logo="slack" href="/docs/plugins/notifiers/slack" description="Slack incoming webhooks." />
|
<PluginCard name="Slack" logo="slack" href="/docs/plugins/notifiers/slack" description="Slack incoming webhooks." />
|
||||||
<PluginCard name="Webhook" logo="webhook" href="/docs/plugins/notifiers/webhook" description="Generic HTTP POST with retries and exponential backoff." />
|
<PluginCard
|
||||||
<PluginCard name="Composio" logo="composio" href="/docs/plugins/notifiers/composio" description="Route through the Composio toolkit — Slack, Discord, or Gmail." />
|
name="Webhook"
|
||||||
<PluginCard name="OpenClaw" logo="openclaw" href="/docs/plugins/notifiers/openclaw" description="Local OpenClaw gateway for personal notifications." />
|
logo="webhook"
|
||||||
|
href="/docs/plugins/notifiers/webhook"
|
||||||
|
description="Generic HTTP POST with retries and exponential backoff."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Composio"
|
||||||
|
logo="composio"
|
||||||
|
href="/docs/plugins/notifiers/composio"
|
||||||
|
description="Route through the Composio toolkit — Slack, Discord, or Gmail."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="OpenClaw"
|
||||||
|
logo="openclaw"
|
||||||
|
href="/docs/plugins/notifiers/openclaw"
|
||||||
|
description="Local OpenClaw gateway for personal notifications."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Terminals
|
## Terminals
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="iTerm2" logo="iterm2" href="/docs/plugins/terminals/iterm2" description="Open attached tabs in iTerm2 via AppleScript (macOS only)." />
|
<PluginCard
|
||||||
<PluginCard name="Web" logo="web" href="/docs/plugins/terminals/web" description="Dashboard xterm.js session URL. Cross-platform." />
|
name="iTerm2"
|
||||||
|
logo="iterm2"
|
||||||
|
href="/docs/plugins/terminals/iterm2"
|
||||||
|
description="Open attached tabs in iTerm2 via AppleScript (macOS only)."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Web"
|
||||||
|
logo="web"
|
||||||
|
href="/docs/plugins/terminals/web"
|
||||||
|
description="Dashboard xterm.js session URL. Cross-platform."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Writing your own
|
## Writing your own
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,5 @@
|
||||||
{
|
{
|
||||||
"title": "Plugins",
|
"title": "Plugins",
|
||||||
"defaultOpen": false,
|
"defaultOpen": false,
|
||||||
"pages": [
|
"pages": ["agents", "runtimes", "workspaces", "trackers", "scm", "notifiers", "terminals", "authoring"]
|
||||||
"agents",
|
|
||||||
"runtimes",
|
|
||||||
"workspaces",
|
|
||||||
"trackers",
|
|
||||||
"scm",
|
|
||||||
"notifiers",
|
|
||||||
"terminals",
|
|
||||||
"authoring"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,17 @@ description: Native macOS / Linux notifications. Silent no-op on Windows.
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="apple" size={28} color />
|
<Logo name="apple" size={28} color />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>notifier</code> · Name: <code>desktop</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>notifier</code> · Name: <code>desktop</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="none" note="On Windows this notifier logs a warning and does nothing — use Discord, Slack, or webhook instead." />
|
<PlatformSupport
|
||||||
|
macos="full"
|
||||||
|
linux="full"
|
||||||
|
windows="none"
|
||||||
|
note="On Windows this notifier logs a warning and does nothing — use Discord, Slack, or webhook instead."
|
||||||
|
/>
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
|
@ -37,7 +44,7 @@ notificationRouting:
|
||||||
```
|
```
|
||||||
|
|
||||||
| Config key | Default | What it does |
|
| Config key | Default | What it does |
|
||||||
|---|---|---|
|
| -------------- | ---------------------- | ------------------------------------------------------------ |
|
||||||
| `backend` | `auto` | `ao-app`, `terminal-notifier`, `osascript`, or auto fallback |
|
| `backend` | `auto` | `ao-app`, `terminal-notifier`, `osascript`, or auto fallback |
|
||||||
| `dashboardUrl` | dashboard port | URL opened from desktop notification actions |
|
| `dashboardUrl` | dashboard port | URL opened from desktop notification actions |
|
||||||
| `appPath` | macOS app install path | Custom AO Notifier.app path |
|
| `appPath` | macOS app install path | Custom AO Notifier.app path |
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,43 @@ description: Who gets pinged when an agent needs you. Seven notifiers ship; pick
|
||||||
Notifiers deliver AO events — session stuck, PR opened, review requested, CI failed — to wherever you actually pay attention.
|
Notifiers deliver AO events — session stuck, PR opened, review requested, CI failed — to wherever you actually pay attention.
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="Dashboard" logo="web" href="/docs/plugins/notifiers/dashboard" description="Retained notifications inside the AO dashboard." />
|
<PluginCard
|
||||||
<PluginCard name="Desktop" logo="apple" href="/docs/plugins/notifiers/desktop" description="Native macOS/Linux notifications. No-op on Windows." />
|
name="Dashboard"
|
||||||
<PluginCard name="Discord" logo="discord" href="/docs/plugins/notifiers/discord" description="Discord webhook with rich embeds." />
|
logo="web"
|
||||||
|
href="/docs/plugins/notifiers/dashboard"
|
||||||
|
description="Retained notifications inside the AO dashboard."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Desktop"
|
||||||
|
logo="apple"
|
||||||
|
href="/docs/plugins/notifiers/desktop"
|
||||||
|
description="Native macOS/Linux notifications. No-op on Windows."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Discord"
|
||||||
|
logo="discord"
|
||||||
|
href="/docs/plugins/notifiers/discord"
|
||||||
|
description="Discord webhook with rich embeds."
|
||||||
|
/>
|
||||||
<PluginCard name="Slack" logo="slack" href="/docs/plugins/notifiers/slack" description="Slack incoming webhook." />
|
<PluginCard name="Slack" logo="slack" href="/docs/plugins/notifiers/slack" description="Slack incoming webhook." />
|
||||||
<PluginCard name="Webhook" logo="webhook" href="/docs/plugins/notifiers/webhook" description="Generic HTTP POST. Retries + exponential backoff." />
|
<PluginCard
|
||||||
<PluginCard name="Composio" logo="composio" href="/docs/plugins/notifiers/composio" description="Route through Composio — Slack, Discord, or Gmail." />
|
name="Webhook"
|
||||||
<PluginCard name="OpenClaw" logo="openclaw" href="/docs/plugins/notifiers/openclaw" description="Local OpenClaw gateway for personal alerts." />
|
logo="webhook"
|
||||||
|
href="/docs/plugins/notifiers/webhook"
|
||||||
|
description="Generic HTTP POST. Retries + exponential backoff."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Composio"
|
||||||
|
logo="composio"
|
||||||
|
href="/docs/plugins/notifiers/composio"
|
||||||
|
description="Route through Composio — Slack, Discord, or Gmail."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="OpenClaw"
|
||||||
|
logo="openclaw"
|
||||||
|
href="/docs/plugins/notifiers/openclaw"
|
||||||
|
description="Local OpenClaw gateway for personal alerts."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Stacking notifiers
|
## Stacking notifiers
|
||||||
|
|
@ -57,7 +87,7 @@ a real test message.
|
||||||
## What gets notified
|
## What gets notified
|
||||||
|
|
||||||
| Event | When |
|
| Event | When |
|
||||||
|---|---|
|
| ------------------- | -------------------------------------- |
|
||||||
| `session.spawned` | `ao spawn` succeeds |
|
| `session.spawned` | `ao spawn` succeeds |
|
||||||
| `session.working` | Agent is actively editing code |
|
| `session.working` | Agent is actively editing code |
|
||||||
| `pr.opened` | Agent pushed a branch and opened a PR |
|
| `pr.opened` | Agent pushed a branch and opened a PR |
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Deliver notifications through a local OpenClaw gateway — personal
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="openclaw" size={28} />
|
<Logo name="openclaw" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>notifier</code> · Name: <code>openclaw</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>notifier</code> · Name: <code>openclaw</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||||
|
|
@ -70,7 +72,7 @@ The OpenClaw config keeps the actual secret:
|
||||||
## Config
|
## Config
|
||||||
|
|
||||||
| Key | Default | What it does |
|
| Key | Default | What it does |
|
||||||
|---|---|---|
|
| -------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------- |
|
||||||
| `url` | `http://127.0.0.1:18789/hooks/agent` | OpenClaw hook endpoint |
|
| `url` | `http://127.0.0.1:18789/hooks/agent` | OpenClaw hook endpoint |
|
||||||
| `openclawConfigPath` | `~/.openclaw/openclaw.json` | Local OpenClaw config path that contains `hooks.token` |
|
| `openclawConfigPath` | `~/.openclaw/openclaw.json` | Local OpenClaw config path that contains `hooks.token` |
|
||||||
| `token` | — | Remote/manual fallback. Avoid for local setups because it stores the secret in AO config |
|
| `token` | — | Remote/manual fallback. Avoid for local setups because it stores the secret in AO config |
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,23 @@ description: Where the agent process runs — tmux on macOS/Linux, plain child p
|
||||||
The runtime is where your agent's terminal actually lives. Two options ship:
|
The runtime is where your agent's terminal actually lives. Two options ship:
|
||||||
|
|
||||||
| Plugin | Best for | Binary needed |
|
| Plugin | Best for | Binary needed |
|
||||||
|---|---|---|
|
| ----------------------------------------- | ---------------------------------------------------- | ------------- |
|
||||||
| [tmux](/docs/plugins/runtimes/tmux) | macOS / Linux default. You can attach interactively. | `tmux` |
|
| [tmux](/docs/plugins/runtimes/tmux) | macOS / Linux default. You can attach interactively. | `tmux` |
|
||||||
| [process](/docs/plugins/runtimes/process) | Windows, Docker, CI-like environments | — |
|
| [process](/docs/plugins/runtimes/process) | Windows, Docker, CI-like environments | — |
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="tmux" logo="tmux" href="/docs/plugins/runtimes/tmux" description="Each agent gets its own tmux window; attach with `ao session attach`." />
|
<PluginCard
|
||||||
<PluginCard name="process" logo="windows" href="/docs/plugins/runtimes/process" description="Cross-platform child process. Required on Windows." />
|
name="tmux"
|
||||||
|
logo="tmux"
|
||||||
|
href="/docs/plugins/runtimes/tmux"
|
||||||
|
description="Each agent gets its own tmux window; attach with `ao session attach`."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="process"
|
||||||
|
logo="windows"
|
||||||
|
href="/docs/plugins/runtimes/process"
|
||||||
|
description="Cross-platform child process. Required on Windows."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Choosing
|
## Choosing
|
||||||
|
|
@ -20,4 +30,4 @@ The runtime is where your agent's terminal actually lives. Two options ship:
|
||||||
- If you can install `tmux` and you want to occasionally drop into a live session, use `runtime: tmux`.
|
- If you can install `tmux` and you want to occasionally drop into a live session, use `runtime: tmux`.
|
||||||
- If you're on Windows, in a container, or just want fewer moving parts, use `runtime: process`.
|
- If you're on Windows, in a container, or just want fewer moving parts, use `runtime: process`.
|
||||||
|
|
||||||
Both runtimes expose the agent's terminal to the dashboard's xterm.js session — attaching via tmux is a *bonus*, not a requirement.
|
Both runtimes expose the agent's terminal to the dashboard's xterm.js session — attaching via tmux is a _bonus_, not a requirement.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Cross-platform child-process runtime. Required on Windows.
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="windows" size={28} color />
|
<Logo name="windows" size={28} color />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>runtime</code> · Name: <code>process</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>runtime</code> · Name: <code>process</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Spawns agents as plain child processes — no tmux involved. Use this on Windows (where tmux isn't available) and in any environment where you'd rather not depend on tmux.
|
Spawns agents as plain child processes — no tmux involved. Use this on Windows (where tmux isn't available) and in any environment where you'd rather not depend on tmux.
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,23 @@ description: Default runtime on macOS and Linux. Each agent gets its own tmux wi
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="tmux" size={28} />
|
<Logo name="tmux" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>runtime</code> · Name: <code>tmux</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>runtime</code> · Name: <code>tmux</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
AO uses [tmux](https://github.com/tmux/tmux) as the default runtime on macOS and Linux. Each session lives in its own tmux window under a single AO-managed session, and you can attach to any of them interactively.
|
AO uses [tmux](https://github.com/tmux/tmux) as the default runtime on macOS and Linux. Each session lives in its own tmux window under a single AO-managed session, and you can attach to any of them interactively.
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="none" note={<>Windows has no tmux. Use <a href="/docs/plugins/runtimes/process">process</a> instead.</>} />
|
<PlatformSupport
|
||||||
|
macos="full"
|
||||||
|
linux="full"
|
||||||
|
windows="none"
|
||||||
|
note={
|
||||||
|
<>
|
||||||
|
Windows has no tmux. Use <a href="/docs/plugins/runtimes/process">process</a> instead.
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: PRs, reviews, and CI status via the gh CLI. Optional webhook for re
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="github" size={28} />
|
<Logo name="github" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>scm</code> · Name: <code>github</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>scm</code> · Name: <code>github</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||||
|
|
@ -59,7 +61,7 @@ projects:
|
||||||
Full `webhook.*` sub-object:
|
Full `webhook.*` sub-object:
|
||||||
|
|
||||||
| Field | Default | Description |
|
| Field | Default | Description |
|
||||||
|---|---|---|
|
| ----------------- | --------------------- | ------------------------------------------- |
|
||||||
| `enabled` | `true` | Enable or disable webhook processing |
|
| `enabled` | `true` | Enable or disable webhook processing |
|
||||||
| `path` | `/api/webhooks` | Override the receive path |
|
| `path` | `/api/webhooks` | Override the receive path |
|
||||||
| `secretEnvVar` | — | Name of the env var holding the HMAC secret |
|
| `secretEnvVar` | — | Name of the env var holding the HMAC secret |
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Merge requests, discussions, and pipelines via the glab CLI.
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="gitlab" size={28} color />
|
<Logo name="gitlab" size={28} color />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>scm</code> · Name: <code>gitlab</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>scm</code> · Name: <code>gitlab</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||||
|
|
@ -25,7 +27,7 @@ scmConfig:
|
||||||
## Mapping
|
## Mapping
|
||||||
|
|
||||||
| Concept | GitHub term | GitLab term |
|
| Concept | GitHub term | GitLab term |
|
||||||
|---|---|---|
|
| -------------- | ------------ | ----------------- |
|
||||||
| Review request | Pull request | Merge request |
|
| Review request | Pull request | Merge request |
|
||||||
| Review | Review | Discussion / note |
|
| Review | Review | Discussion / note |
|
||||||
| CI status | Check runs | Pipelines / jobs |
|
| CI status | Check runs | Pipelines / jobs |
|
||||||
|
|
@ -65,7 +67,7 @@ projects:
|
||||||
Full `webhook.*` sub-object:
|
Full `webhook.*` sub-object:
|
||||||
|
|
||||||
| Field | Default | Description |
|
| Field | Default | Description |
|
||||||
|---|---|---|
|
| ----------------- | --------------------- | ---------------------------------------- |
|
||||||
| `enabled` | `true` | Enable or disable webhook processing |
|
| `enabled` | `true` | Enable or disable webhook processing |
|
||||||
| `path` | `/api/webhooks` | Override the receive path |
|
| `path` | `/api/webhooks` | Override the receive path |
|
||||||
| `secretEnvVar` | — | Name of the env var holding the token |
|
| `secretEnvVar` | — | Name of the env var holding the token |
|
||||||
|
|
@ -85,7 +87,7 @@ AO ignores review comments from known bot accounts so they don't block the merge
|
||||||
**Hardcoded bots:**
|
**Hardcoded bots:**
|
||||||
|
|
||||||
| Username |
|
| Username |
|
||||||
|---|
|
| ------------------ |
|
||||||
| `gitlab-bot` |
|
| `gitlab-bot` |
|
||||||
| `ghost` |
|
| `ghost` |
|
||||||
| `dependabot[bot]` |
|
| `dependabot[bot]` |
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,18 @@ description: PRs, reviews, CI status. The tracker watches issues; the SCM plugin
|
||||||
The **SCM** plugin is everything to do with the pull/merge-request side: opening PRs, reading their review state, and polling CI checks. Two ship.
|
The **SCM** plugin is everything to do with the pull/merge-request side: opening PRs, reading their review state, and polling CI checks. Two ship.
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="GitHub" logo="github" href="/docs/plugins/scm/github" description="PRs, reviews, and CI status via gh." />
|
<PluginCard
|
||||||
<PluginCard name="GitLab" logo="gitlab" href="/docs/plugins/scm/gitlab" description="MRs, discussions, pipelines via glab." />
|
name="GitHub"
|
||||||
|
logo="github"
|
||||||
|
href="/docs/plugins/scm/github"
|
||||||
|
description="PRs, reviews, and CI status via gh."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="GitLab"
|
||||||
|
logo="gitlab"
|
||||||
|
href="/docs/plugins/scm/gitlab"
|
||||||
|
description="MRs, discussions, pipelines via glab."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Why separate from the tracker
|
## Why separate from the tracker
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,18 @@ description: How you attach to a running agent. iTerm2 on macOS, or the dashboar
|
||||||
The **terminal** plugin is what `ao open` and `ao session attach` use. If you only ever look at the dashboard, you don't need to think about it.
|
The **terminal** plugin is what `ao open` and `ao session attach` use. If you only ever look at the dashboard, you don't need to think about it.
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="iTerm2" logo="iterm2" href="/docs/plugins/terminals/iterm2" description="Open attached tabs in iTerm2 via AppleScript (macOS only)." />
|
<PluginCard
|
||||||
<PluginCard name="Web" logo="web" href="/docs/plugins/terminals/web" description="Dashboard xterm.js session URL. Cross-platform." />
|
name="iTerm2"
|
||||||
|
logo="iterm2"
|
||||||
|
href="/docs/plugins/terminals/iterm2"
|
||||||
|
description="Open attached tabs in iTerm2 via AppleScript (macOS only)."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Web"
|
||||||
|
logo="web"
|
||||||
|
href="/docs/plugins/terminals/web"
|
||||||
|
description="Dashboard xterm.js session URL. Cross-platform."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Default
|
## Default
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Open attached tabs in iTerm2 via AppleScript. macOS only.
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="iterm2" size={28} />
|
<Logo name="iterm2" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>terminal</code> · Name: <code>iterm2</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>terminal</code> · Name: <code>iterm2</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="none" windows="none" />
|
<PlatformSupport macos="full" linux="none" windows="none" />
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Dashboard xterm.js terminal. Cross-platform. The only option on Win
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="web" size={28} />
|
<Logo name="web" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>terminal</code> · Name: <code>web</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>terminal</code> · Name: <code>web</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||||
|
|
@ -21,7 +23,7 @@ terminalConfig:
|
||||||
```
|
```
|
||||||
|
|
||||||
| Config key | Default | What it does |
|
| Config key | Default | What it does |
|
||||||
|---|---|---|
|
| -------------- | ----------------------- | ---------------------------------------------------------------------------------- |
|
||||||
| `dashboardUrl` | `http://localhost:3000` | Base URL for the dashboard. Override when the dashboard runs on another host/port. |
|
| `dashboardUrl` | `http://localhost:3000` | Base URL for the dashboard. Override when the dashboard runs on another host/port. |
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Issues via the gh CLI. Zero API tokens to manage — gh auth login
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="github" size={28} />
|
<Logo name="github" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>tracker</code> · Name: <code>github</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>tracker</code> · Name: <code>github</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||||
|
|
@ -32,7 +34,7 @@ projects:
|
||||||
## How it's used
|
## How it's used
|
||||||
|
|
||||||
| Operation | `gh` command invoked |
|
| Operation | `gh` command invoked |
|
||||||
|---|---|
|
| ---------------- | ------------------------------------------- |
|
||||||
| Fetch issue | `gh issue view <num> --json title,body,...` |
|
| Fetch issue | `gh issue view <num> --json title,body,...` |
|
||||||
| Create issue | `gh issue create` |
|
| Create issue | `gh issue create` |
|
||||||
| Comment on issue | `gh issue comment` |
|
| Comment on issue | `gh issue comment` |
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: GitLab issues via the glab CLI. Self-hosted instances supported.
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="gitlab" size={28} color />
|
<Logo name="gitlab" size={28} color />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>tracker</code> · Name: <code>gitlab</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>tracker</code> · Name: <code>gitlab</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||||
|
|
@ -28,11 +30,12 @@ projects:
|
||||||
```
|
```
|
||||||
|
|
||||||
| Config key | Default | What it does |
|
| Config key | Default | What it does |
|
||||||
|---|---|---|
|
| ---------- | ------------ | ---------------------------------------------------- |
|
||||||
| `host` | `gitlab.com` | GitLab hostname — override for self-hosted instances |
|
| `host` | `gitlab.com` | GitLab hostname — override for self-hosted instances |
|
||||||
|
|
||||||
<Callout type="warn">
|
<Callout type="warn">
|
||||||
`host` belongs on the **`tracker`** block (plugin-level config), not inside `trackerConfig`. `trackerConfig` is for per-project passthrough fields (labels, assignee, milestone). Putting `host` inside `trackerConfig` will not be read.
|
`host` belongs on the **`tracker`** block (plugin-level config), not inside `trackerConfig`. `trackerConfig` is for
|
||||||
|
per-project passthrough fields (labels, assignee, milestone). Putting `host` inside `trackerConfig` will not be read.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Self-hosted
|
## Self-hosted
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,24 @@ description: Where your issues live. AO fetches them, assigns them to sessions,
|
||||||
The **tracker** plugin is how AO fetches issues, creates new ones, and links sessions to them. Three trackers ship.
|
The **tracker** plugin is how AO fetches issues, creates new ones, and links sessions to them. Three trackers ship.
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="GitHub" logo="github" href="/docs/plugins/trackers/github" description="Issues + PRs via the gh CLI. Default." />
|
<PluginCard
|
||||||
<PluginCard name="GitLab" logo="gitlab" href="/docs/plugins/trackers/gitlab" description="Issues + MRs via the glab CLI. Self-hosted supported." />
|
name="GitHub"
|
||||||
<PluginCard name="Linear" logo="linear" href="/docs/plugins/trackers/linear" description="Linear issues. Direct API key or Composio-mediated." />
|
logo="github"
|
||||||
|
href="/docs/plugins/trackers/github"
|
||||||
|
description="Issues + PRs via the gh CLI. Default."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="GitLab"
|
||||||
|
logo="gitlab"
|
||||||
|
href="/docs/plugins/trackers/gitlab"
|
||||||
|
description="Issues + MRs via the glab CLI. Self-hosted supported."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="Linear"
|
||||||
|
logo="linear"
|
||||||
|
href="/docs/plugins/trackers/linear"
|
||||||
|
description="Linear issues. Direct API key or Composio-mediated."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## What a tracker does
|
## What a tracker does
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Linear issues. Direct API key or Composio-mediated — AO picks the
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="linear" size={28} color />
|
<Logo name="linear" size={28} color />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>tracker</code> · Name: <code>linear</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>tracker</code> · Name: <code>linear</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||||
|
|
@ -49,7 +51,7 @@ AO detects this and routes Linear calls through Composio — no separate Linear
|
||||||
## Per-project config
|
## Per-project config
|
||||||
|
|
||||||
| Key | Required | What it does |
|
| Key | Required | What it does |
|
||||||
|---|---|---|
|
| --------------- | --------------------- | ---------------------------------------------------------- |
|
||||||
| `teamId` | ✓ (for `createIssue`) | Linear team the issue lives in |
|
| `teamId` | ✓ (for `createIssue`) | Linear team the issue lives in |
|
||||||
| `workspaceSlug` | optional | Used to render `https://linear.app/{slug}/issue/{id}` URLs |
|
| `workspaceSlug` | optional | Used to render `https://linear.app/{slug}/issue/{id}` URLs |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Full per-session git clone. Use when worktrees fight your tooling.
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="github" size={28} />
|
<Logo name="github" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>workspace</code> · Name: <code>clone</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>workspace</code> · Name: <code>clone</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Every session gets a full `git clone`. Heavier on disk than `worktree`, but simpler for any tooling that gets confused by the worktree layout.
|
Every session gets a full `git clone`. Heavier on disk than `worktree`, but simpler for any tooling that gets confused by the worktree layout.
|
||||||
|
|
@ -21,7 +23,7 @@ workspaceConfig:
|
||||||
```
|
```
|
||||||
|
|
||||||
| Config key | Default | What it does |
|
| Config key | Default | What it does |
|
||||||
|---|---|---|
|
| ---------- | -------------- | ----------------------------- |
|
||||||
| `cloneDir` | `~/.ao-clones` | Base directory for all clones |
|
| `cloneDir` | `~/.ao-clones` | Base directory for all clones |
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,23 @@ description: How each agent gets its own copy of the repo — a git worktree or
|
||||||
Every session needs an isolated filesystem so agents don't stomp each other. AO handles this with a **workspace** plugin. Two options ship:
|
Every session needs an isolated filesystem so agents don't stomp each other. AO handles this with a **workspace** plugin. Two options ship:
|
||||||
|
|
||||||
| Plugin | Isolation | Disk usage | Speed |
|
| Plugin | Isolation | Disk usage | Speed |
|
||||||
|---|---|---|---|
|
| --------------------------------------------- | --------------------------------- | ---------------------- | ---------------------- |
|
||||||
| [worktree](/docs/plugins/workspaces/worktree) | Per-session branch, shared `.git` | Low (shared object DB) | Fast (no clone) |
|
| [worktree](/docs/plugins/workspaces/worktree) | Per-session branch, shared `.git` | Low (shared object DB) | Fast (no clone) |
|
||||||
| [clone](/docs/plugins/workspaces/clone) | Per-session full clone | High | Slower (initial clone) |
|
| [clone](/docs/plugins/workspaces/clone) | Per-session full clone | High | Slower (initial clone) |
|
||||||
|
|
||||||
<PluginGrid>
|
<PluginGrid>
|
||||||
<PluginCard name="worktree" logo="github" href="/docs/plugins/workspaces/worktree" description="git worktree. Default, fast, disk-efficient." />
|
<PluginCard
|
||||||
<PluginCard name="clone" logo="github" href="/docs/plugins/workspaces/clone" description="Full per-session clone. Use when tools struggle with worktrees." />
|
name="worktree"
|
||||||
|
logo="github"
|
||||||
|
href="/docs/plugins/workspaces/worktree"
|
||||||
|
description="git worktree. Default, fast, disk-efficient."
|
||||||
|
/>
|
||||||
|
<PluginCard
|
||||||
|
name="clone"
|
||||||
|
logo="github"
|
||||||
|
href="/docs/plugins/workspaces/clone"
|
||||||
|
description="Full per-session clone. Use when tools struggle with worktrees."
|
||||||
|
/>
|
||||||
</PluginGrid>
|
</PluginGrid>
|
||||||
|
|
||||||
## Choosing
|
## Choosing
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ description: Default workspace. Each session is a git worktree pointing at a fre
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: "0.5rem 0 1.25rem" }}>
|
||||||
<Logo name="github" size={28} />
|
<Logo name="github" size={28} />
|
||||||
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>Slot: <code>workspace</code> · Name: <code>worktree</code></span>
|
<span style={{ fontSize: "0.8125rem", color: "var(--color-fd-muted-foreground)" }}>
|
||||||
|
Slot: <code>workspace</code> · Name: <code>worktree</code>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The default workspace plugin. Each session gets a [git worktree](https://git-scm.com/docs/git-worktree) with its own branch. All worktrees share the underlying `.git` object database, so disk usage stays low even with dozens of parallel agents.
|
The default workspace plugin. Each session gets a [git worktree](https://git-scm.com/docs/git-worktree) with its own branch. All worktrees share the underlying `.git` object database, so disk usage stays low even with dozens of parallel agents.
|
||||||
|
|
@ -21,7 +23,7 @@ workspaceConfig:
|
||||||
```
|
```
|
||||||
|
|
||||||
| Config key | Default | What it does |
|
| Config key | Default | What it does |
|
||||||
|---|---|---|
|
| ------------- | -------------- | -------------------------------- |
|
||||||
| `worktreeDir` | `~/.worktrees` | Base directory for all worktrees |
|
| `worktreeDir` | `~/.worktrees` | Base directory for all worktrees |
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
@ -45,7 +47,7 @@ projects:
|
||||||
```
|
```
|
||||||
|
|
||||||
| Knob | Purpose |
|
| Knob | Purpose |
|
||||||
|---|---|
|
| ------------ | ------------------------------------------------------------------------------------------------- |
|
||||||
| `symlinks` | Files/dirs to symlink from the source repo into each worktree (e.g. `.env.local`, a shared cache) |
|
| `symlinks` | Files/dirs to symlink from the source repo into each worktree (e.g. `.env.local`, a shared cache) |
|
||||||
| `postCreate` | Shell commands to run in each new worktree after creation (e.g. `pnpm install`) |
|
| `postCreate` | Shell commands to run in each new worktree after creation (e.g. `pnpm install`) |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ import { Step, Steps } from "fumadocs-ui/components/steps";
|
||||||
This quickstart walks through the smallest useful AO loop: start the dashboard, create one worker session, watch it work, and clean it up after the PR is merged.
|
This quickstart walks through the smallest useful AO loop: start the dashboard, create one worker session, watch it work, and clean it up after the PR is merged.
|
||||||
|
|
||||||
<Callout type="info" title="Before you start">
|
<Callout type="info" title="Before you start">
|
||||||
Complete [Installation](/docs/installation) first. You need `ao`, Git, one authenticated source-control CLI such as `gh`, and one signed-in agent CLI.
|
Complete [Installation](/docs/installation) first. You need `ao`, Git, one authenticated source-control CLI such as
|
||||||
|
`gh`, and one signed-in agent CLI.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
## Pick A Safe First Task
|
## Pick A Safe First Task
|
||||||
|
|
@ -55,14 +56,16 @@ Open a second terminal in the same repository.
|
||||||
ao spawn 42
|
ao spawn 42
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `42` with the issue number. AO fetches the issue through `gh`, creates a worktree, starts the configured worker agent, and gives it the issue context.
|
Replace `42` with the issue number. AO fetches the issue through `gh`, creates a worktree, starts the configured worker agent, and gives it the issue context.
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab value="Freeform prompt">
|
<Tab value="Freeform prompt">
|
||||||
```bash
|
```bash
|
||||||
ao spawn --prompt "Update the README install section to mention Node 20"
|
ao spawn --prompt "Update the README install section to mention Node 20"
|
||||||
```
|
```
|
||||||
|
|
||||||
Use this when the task is not tracked in GitHub, GitLab, or Linear yet. Keep the prompt specific and reviewable.
|
Use this when the task is not tracked in GitHub, GitLab, or Linear yet. Keep the prompt specific and reviewable.
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
|
@ -127,7 +130,7 @@ ao session cleanup
|
||||||
## What AO Created
|
## What AO Created
|
||||||
|
|
||||||
| Item | What it means |
|
| Item | What it means |
|
||||||
| --- | --- |
|
| ------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||||
| `agent-orchestrator.yaml` | Project config: plugins, projects, reactions, runtime, and notifier choices. |
|
| `agent-orchestrator.yaml` | Project config: plugins, projects, reactions, runtime, and notifier choices. |
|
||||||
| Orchestrator session | A coordinating session started by `ao start`; it supervises worker sessions. |
|
| Orchestrator session | A coordinating session started by `ao start`; it supervises worker sessions. |
|
||||||
| Worker session | The agent process that works on one issue or prompt. |
|
| Worker session | The agent process that works on one issue or prompt. |
|
||||||
|
|
@ -137,7 +140,7 @@ ao session cleanup
|
||||||
## If Something Looks Wrong
|
## If Something Looks Wrong
|
||||||
|
|
||||||
| Symptom | First check |
|
| Symptom | First check |
|
||||||
| --- | --- |
|
| --------------------------------------- | ----------------------------------------------------------------------------- |
|
||||||
| Dashboard is not updating | Make sure the `ao start` terminal is still running. |
|
| Dashboard is not updating | Make sure the `ao start` terminal is still running. |
|
||||||
| `ao spawn` warns that AO is not running | Start AO with `ao start` before spawning. |
|
| `ao spawn` warns that AO is not running | Start AO with `ao start` before spawning. |
|
||||||
| GitHub issue or PR data is missing | Run `gh auth status` and check the `repo` field in `agent-orchestrator.yaml`. |
|
| GitHub issue or PR data is missing | Run `gh auth status` and check the `repo` field in `agent-orchestrator.yaml`. |
|
||||||
|
|
@ -147,8 +150,20 @@ ao session cleanup
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card title="Parallel issues" description="Spawn several worker sessions without duplicating active work." href="/docs/guides/parallel-issues" />
|
<Card
|
||||||
|
title="Parallel issues"
|
||||||
|
description="Spawn several worker sessions without duplicating active work."
|
||||||
|
href="/docs/guides/parallel-issues"
|
||||||
|
/>
|
||||||
<Card title="CI recovery" description="See how AO reacts when checks fail." href="/docs/guides/ci-recovery" />
|
<Card title="CI recovery" description="See how AO reacts when checks fail." href="/docs/guides/ci-recovery" />
|
||||||
<Card title="Configuration" description="Tune agents, runtimes, projects, reactions, and notifiers." href="/docs/configuration" />
|
<Card
|
||||||
<Card title="Dashboard" description="Understand session cards, attention zones, and live terminal views." href="/docs/dashboard" />
|
title="Configuration"
|
||||||
|
description="Tune agents, runtimes, projects, reactions, and notifiers."
|
||||||
|
href="/docs/configuration"
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
title="Dashboard"
|
||||||
|
description="Understand session cards, attention zones, and live terminal views."
|
||||||
|
href="/docs/dashboard"
|
||||||
|
/>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,17 @@ Covers: install health, plugin resolution, notifier connectivity, stale temp fil
|
||||||
|
|
||||||
<Accordions>
|
<Accordions>
|
||||||
<Accordion title="GitHub API rate limit">
|
<Accordion title="GitHub API rate limit">
|
||||||
AO paces calls, but heavy projects can still hit limits. The lifecycle manager backs off automatically. If you see sustained rate-limit errors, check that you're logged in as yourself (`gh auth status`) — unauthenticated calls have much lower limits.
|
AO paces calls, but heavy projects can still hit limits. The lifecycle manager backs off automatically. If you see
|
||||||
|
sustained rate-limit errors, check that you're logged in as yourself (`gh auth status`) — unauthenticated calls have
|
||||||
|
much lower limits.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="CI recovery never fires">
|
<Accordion title="CI recovery never fires">
|
||||||
`reactions.ciFailed.enabled: false` in your config, or the PR is in draft. See [CI recovery › When it doesn't kick in](/docs/guides/ci-recovery#when-it-doesnt-kick-in).
|
`reactions.ciFailed.enabled: false` in your config, or the PR is in draft. See [CI recovery › When it doesn't kick
|
||||||
|
in](/docs/guides/ci-recovery#when-it-doesnt-kick-in).
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Review loop never fires">
|
<Accordion title="Review loop never fires">
|
||||||
Review feedback is only replayed on `CHANGES_REQUESTED` — not on plain `COMMENTED` reviews. See [Review loop](/docs/guides/review-loop).
|
Review feedback is only replayed on `CHANGES_REQUESTED` — not on plain `COMMENTED` reviews. See [Review
|
||||||
|
loop](/docs/guides/review-loop).
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Webhook delivers but nothing happens">
|
<Accordion title="Webhook delivers but nothing happens">
|
||||||
HMAC signature check failing. Verify your `secretEnvVar` is exported and matches the secret you set on GitHub.
|
HMAC signature check failing. Verify your `secretEnvVar` is exported and matches the secret you set on GitHub.
|
||||||
|
|
@ -82,7 +86,8 @@ Covers: install health, plugin resolution, notifier connectivity, stale temp fil
|
||||||
Expected. Add Discord, Slack, or a webhook notifier in the same `notifier:` list.
|
Expected. Add Discord, Slack, or a webhook notifier in the same `notifier:` list.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Slack webhook 429s under load">
|
<Accordion title="Slack webhook 429s under load">
|
||||||
The slack notifier doesn't honor Retry-After. Use the generic [webhook notifier](/docs/plugins/notifiers/webhook) with `retries` tuned up.
|
The slack notifier doesn't honor Retry-After. Use the generic [webhook notifier](/docs/plugins/notifiers/webhook)
|
||||||
|
with `retries` tuned up.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Accordions>
|
</Accordions>
|
||||||
|
|
||||||
|
|
@ -101,7 +106,7 @@ Covers: install health, plugin resolution, notifier connectivity, stale temp fil
|
||||||
|
|
||||||
- Check the [FAQ](/docs/faq).
|
- Check the [FAQ](/docs/faq).
|
||||||
- Open an issue: [`ComposioHQ/agent-orchestrator`](https://github.com/ComposioHQ/agent-orchestrator/issues).
|
- Open an issue: [`ComposioHQ/agent-orchestrator`](https://github.com/ComposioHQ/agent-orchestrator/issues).
|
||||||
When opening an issue, include:
|
When opening an issue, include:
|
||||||
|
|
||||||
- Output of `ao --version`
|
- Output of `ao --version`
|
||||||
- The contents of `~/.agent-orchestrator/{hash}-observability/processes/` (one JSON snapshot per process; each contains traces, health, and metrics)
|
- The contents of `~/.agent-orchestrator/{hash}-observability/processes/` (one JSON snapshot per process; each contains traces, health, and metrics)
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,13 @@ const FALLBACK_STATS: GitHubRepoStats = {
|
||||||
|
|
||||||
export async function getGitHubRepoStats(): Promise<GitHubRepoStats> {
|
export async function getGitHubRepoStats(): Promise<GitHubRepoStats> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch("https://api.github.com/repos/ComposioHQ/agent-orchestrator", {
|
||||||
"https://api.github.com/repos/ComposioHQ/agent-orchestrator",
|
|
||||||
{
|
|
||||||
next: { revalidate: 3600 },
|
next: { revalidate: 3600 },
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/vnd.github+json",
|
Accept: "application/vnd.github+json",
|
||||||
"User-Agent": "ao-website",
|
"User-Agent": "ao-website",
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return FALLBACK_STATS;
|
return FALLBACK_STATS;
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,9 @@ a {
|
||||||
.landing-card {
|
.landing-card {
|
||||||
background: var(--landing-card-bg);
|
background: var(--landing-card-bg);
|
||||||
border: 1px solid var(--landing-border-subtle);
|
border: 1px solid var(--landing-border-subtle);
|
||||||
transition: border-color 0.2s, background 0.2s;
|
transition:
|
||||||
|
border-color 0.2s,
|
||||||
|
background 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing-card:hover {
|
.landing-card:hover {
|
||||||
|
|
@ -155,7 +157,9 @@ a {
|
||||||
.landing-reveal {
|
.landing-reveal {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(32px);
|
transform: translateY(32px);
|
||||||
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
|
transition:
|
||||||
|
opacity 0.8s ease-out,
|
||||||
|
transform 0.8s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing-reveal.visible {
|
.landing-reveal.visible {
|
||||||
|
|
@ -413,110 +417,288 @@ a {
|
||||||
These unlayered rules (outside any @layer) beat ALL named layers and restore
|
These unlayered rules (outside any @layer) beat ALL named layers and restore
|
||||||
the correct values for every margin/padding class used in the landing page. */
|
the correct values for every margin/padding class used in the landing page. */
|
||||||
|
|
||||||
.landing-page .mt-1 { margin-top: calc(var(--spacing) * 1); }
|
.landing-page .mt-1 {
|
||||||
.landing-page .mt-6 { margin-top: calc(var(--spacing) * 6); }
|
margin-top: calc(var(--spacing) * 1);
|
||||||
.landing-page .mt-8 { margin-top: calc(var(--spacing) * 8); }
|
}
|
||||||
.landing-page .mt-10 { margin-top: calc(var(--spacing) * 10); }
|
.landing-page .mt-6 {
|
||||||
.landing-page .mt-12 { margin-top: calc(var(--spacing) * 12); }
|
margin-top: calc(var(--spacing) * 6);
|
||||||
.landing-page .mt-16 { margin-top: calc(var(--spacing) * 16); }
|
}
|
||||||
.landing-page .mt-20 { margin-top: calc(var(--spacing) * 20); }
|
.landing-page .mt-8 {
|
||||||
|
margin-top: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
|
.landing-page .mt-10 {
|
||||||
|
margin-top: calc(var(--spacing) * 10);
|
||||||
|
}
|
||||||
|
.landing-page .mt-12 {
|
||||||
|
margin-top: calc(var(--spacing) * 12);
|
||||||
|
}
|
||||||
|
.landing-page .mt-16 {
|
||||||
|
margin-top: calc(var(--spacing) * 16);
|
||||||
|
}
|
||||||
|
.landing-page .mt-20 {
|
||||||
|
margin-top: calc(var(--spacing) * 20);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .mb-1 { margin-bottom: calc(var(--spacing) * 1); }
|
.landing-page .mb-1 {
|
||||||
.landing-page .mb-2 { margin-bottom: calc(var(--spacing) * 2); }
|
margin-bottom: calc(var(--spacing) * 1);
|
||||||
.landing-page .mb-3 { margin-bottom: calc(var(--spacing) * 3); }
|
}
|
||||||
.landing-page .mb-4 { margin-bottom: calc(var(--spacing) * 4); }
|
.landing-page .mb-2 {
|
||||||
.landing-page .mb-5 { margin-bottom: calc(var(--spacing) * 5); }
|
margin-bottom: calc(var(--spacing) * 2);
|
||||||
.landing-page .mb-6 { margin-bottom: calc(var(--spacing) * 6); }
|
}
|
||||||
.landing-page .mb-8 { margin-bottom: calc(var(--spacing) * 8); }
|
.landing-page .mb-3 {
|
||||||
.landing-page .mb-10 { margin-bottom: calc(var(--spacing) * 10); }
|
margin-bottom: calc(var(--spacing) * 3);
|
||||||
.landing-page .mb-12 { margin-bottom: calc(var(--spacing) * 12); }
|
}
|
||||||
.landing-page .mb-16 { margin-bottom: calc(var(--spacing) * 16); }
|
.landing-page .mb-4 {
|
||||||
.landing-page .mb-1\.5 { margin-bottom: calc(var(--spacing) * 1.5); }
|
margin-bottom: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
|
.landing-page .mb-5 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 5);
|
||||||
|
}
|
||||||
|
.landing-page .mb-6 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
.landing-page .mb-8 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
|
.landing-page .mb-10 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 10);
|
||||||
|
}
|
||||||
|
.landing-page .mb-12 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 12);
|
||||||
|
}
|
||||||
|
.landing-page .mb-16 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 16);
|
||||||
|
}
|
||||||
|
.landing-page .mb-1\.5 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .ml-1 { margin-left: calc(var(--spacing) * 1); }
|
.landing-page .ml-1 {
|
||||||
.landing-page .ml-2 { margin-left: calc(var(--spacing) * 2); }
|
margin-left: calc(var(--spacing) * 1);
|
||||||
.landing-page .ml-1\.5 { margin-left: calc(var(--spacing) * 1.5); }
|
}
|
||||||
|
.landing-page .ml-2 {
|
||||||
|
margin-left: calc(var(--spacing) * 2);
|
||||||
|
}
|
||||||
|
.landing-page .ml-1\.5 {
|
||||||
|
margin-left: calc(var(--spacing) * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .mr-1 { margin-right: calc(var(--spacing) * 1); }
|
.landing-page .mr-1 {
|
||||||
.landing-page .mr-1\.5 { margin-right: calc(var(--spacing) * 1.5); }
|
margin-right: calc(var(--spacing) * 1);
|
||||||
|
}
|
||||||
|
.landing-page .mr-1\.5 {
|
||||||
|
margin-right: calc(var(--spacing) * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .p-2 { padding: calc(var(--spacing) * 2); }
|
.landing-page .p-2 {
|
||||||
.landing-page .p-3 { padding: calc(var(--spacing) * 3); }
|
padding: calc(var(--spacing) * 2);
|
||||||
.landing-page .p-6 { padding: calc(var(--spacing) * 6); }
|
}
|
||||||
.landing-page .p-7 { padding: calc(var(--spacing) * 7); }
|
.landing-page .p-3 {
|
||||||
.landing-page .p-8 { padding: calc(var(--spacing) * 8); }
|
padding: calc(var(--spacing) * 3);
|
||||||
.landing-page .p-2\.5 { padding: calc(var(--spacing) * 2.5); }
|
}
|
||||||
|
.landing-page .p-6 {
|
||||||
|
padding: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
.landing-page .p-7 {
|
||||||
|
padding: calc(var(--spacing) * 7);
|
||||||
|
}
|
||||||
|
.landing-page .p-8 {
|
||||||
|
padding: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
|
.landing-page .p-2\.5 {
|
||||||
|
padding: calc(var(--spacing) * 2.5);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .pt-10 { padding-top: calc(var(--spacing) * 10); }
|
.landing-page .pt-10 {
|
||||||
.landing-page .pt-32 { padding-top: calc(var(--spacing) * 32); }
|
padding-top: calc(var(--spacing) * 10);
|
||||||
|
}
|
||||||
|
.landing-page .pt-32 {
|
||||||
|
padding-top: calc(var(--spacing) * 32);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .pb-20 { padding-bottom: calc(var(--spacing) * 20); }
|
.landing-page .pb-20 {
|
||||||
|
padding-bottom: calc(var(--spacing) * 20);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .px-2 { padding-left: calc(var(--spacing) * 2); padding-right: calc(var(--spacing) * 2); }
|
.landing-page .px-2 {
|
||||||
.landing-page .px-3 { padding-left: calc(var(--spacing) * 3); padding-right: calc(var(--spacing) * 3); }
|
padding-left: calc(var(--spacing) * 2);
|
||||||
.landing-page .px-4 { padding-left: calc(var(--spacing) * 4); padding-right: calc(var(--spacing) * 4); }
|
padding-right: calc(var(--spacing) * 2);
|
||||||
.landing-page .px-5 { padding-left: calc(var(--spacing) * 5); padding-right: calc(var(--spacing) * 5); }
|
}
|
||||||
.landing-page .px-6 { padding-left: calc(var(--spacing) * 6); padding-right: calc(var(--spacing) * 6); }
|
.landing-page .px-3 {
|
||||||
.landing-page .px-8 { padding-left: calc(var(--spacing) * 8); padding-right: calc(var(--spacing) * 8); }
|
padding-left: calc(var(--spacing) * 3);
|
||||||
.landing-page .px-3\.5 { padding-left: calc(var(--spacing) * 3.5); padding-right: calc(var(--spacing) * 3.5); }
|
padding-right: calc(var(--spacing) * 3);
|
||||||
|
}
|
||||||
|
.landing-page .px-4 {
|
||||||
|
padding-left: calc(var(--spacing) * 4);
|
||||||
|
padding-right: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
|
.landing-page .px-5 {
|
||||||
|
padding-left: calc(var(--spacing) * 5);
|
||||||
|
padding-right: calc(var(--spacing) * 5);
|
||||||
|
}
|
||||||
|
.landing-page .px-6 {
|
||||||
|
padding-left: calc(var(--spacing) * 6);
|
||||||
|
padding-right: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
.landing-page .px-8 {
|
||||||
|
padding-left: calc(var(--spacing) * 8);
|
||||||
|
padding-right: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
|
.landing-page .px-3\.5 {
|
||||||
|
padding-left: calc(var(--spacing) * 3.5);
|
||||||
|
padding-right: calc(var(--spacing) * 3.5);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .py-1 { padding-top: calc(var(--spacing) * 1); padding-bottom: calc(var(--spacing) * 1); }
|
.landing-page .py-1 {
|
||||||
.landing-page .py-2 { padding-top: calc(var(--spacing) * 2); padding-bottom: calc(var(--spacing) * 2); }
|
padding-top: calc(var(--spacing) * 1);
|
||||||
.landing-page .py-3 { padding-top: calc(var(--spacing) * 3); padding-bottom: calc(var(--spacing) * 3); }
|
padding-bottom: calc(var(--spacing) * 1);
|
||||||
.landing-page .py-4 { padding-top: calc(var(--spacing) * 4); padding-bottom: calc(var(--spacing) * 4); }
|
}
|
||||||
.landing-page .py-6 { padding-top: calc(var(--spacing) * 6); padding-bottom: calc(var(--spacing) * 6); }
|
.landing-page .py-2 {
|
||||||
.landing-page .py-8 { padding-top: calc(var(--spacing) * 8); padding-bottom: calc(var(--spacing) * 8); }
|
padding-top: calc(var(--spacing) * 2);
|
||||||
.landing-page .py-20 { padding-top: calc(var(--spacing) * 20); padding-bottom: calc(var(--spacing) * 20); }
|
padding-bottom: calc(var(--spacing) * 2);
|
||||||
.landing-page .py-40 { padding-top: calc(var(--spacing) * 40); padding-bottom: calc(var(--spacing) * 40); }
|
}
|
||||||
.landing-page .py-1\.5 { padding-top: calc(var(--spacing) * 1.5); padding-bottom: calc(var(--spacing) * 1.5); }
|
.landing-page .py-3 {
|
||||||
.landing-page .py-2\.5 { padding-top: calc(var(--spacing) * 2.5); padding-bottom: calc(var(--spacing) * 2.5); }
|
padding-top: calc(var(--spacing) * 3);
|
||||||
.landing-page .py-3\.5 { padding-top: calc(var(--spacing) * 3.5); padding-bottom: calc(var(--spacing) * 3.5); }
|
padding-bottom: calc(var(--spacing) * 3);
|
||||||
|
}
|
||||||
|
.landing-page .py-4 {
|
||||||
|
padding-top: calc(var(--spacing) * 4);
|
||||||
|
padding-bottom: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
|
.landing-page .py-6 {
|
||||||
|
padding-top: calc(var(--spacing) * 6);
|
||||||
|
padding-bottom: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
.landing-page .py-8 {
|
||||||
|
padding-top: calc(var(--spacing) * 8);
|
||||||
|
padding-bottom: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
|
.landing-page .py-20 {
|
||||||
|
padding-top: calc(var(--spacing) * 20);
|
||||||
|
padding-bottom: calc(var(--spacing) * 20);
|
||||||
|
}
|
||||||
|
.landing-page .py-40 {
|
||||||
|
padding-top: calc(var(--spacing) * 40);
|
||||||
|
padding-bottom: calc(var(--spacing) * 40);
|
||||||
|
}
|
||||||
|
.landing-page .py-1\.5 {
|
||||||
|
padding-top: calc(var(--spacing) * 1.5);
|
||||||
|
padding-bottom: calc(var(--spacing) * 1.5);
|
||||||
|
}
|
||||||
|
.landing-page .py-2\.5 {
|
||||||
|
padding-top: calc(var(--spacing) * 2.5);
|
||||||
|
padding-bottom: calc(var(--spacing) * 2.5);
|
||||||
|
}
|
||||||
|
.landing-page .py-3\.5 {
|
||||||
|
padding-top: calc(var(--spacing) * 3.5);
|
||||||
|
padding-bottom: calc(var(--spacing) * 3.5);
|
||||||
|
}
|
||||||
|
|
||||||
/* Arbitrary padding values */
|
/* Arbitrary padding values */
|
||||||
.landing-page .py-\[100px\] { padding-top: 100px; padding-bottom: 100px; }
|
.landing-page .py-\[100px\] {
|
||||||
.landing-page .py-\[120px\] { padding-top: 120px; padding-bottom: 120px; }
|
padding-top: 100px;
|
||||||
.landing-page .pt-\[60px\] { padding-top: 60px; }
|
padding-bottom: 100px;
|
||||||
.landing-page .pb-\[120px\] { padding-bottom: 120px; }
|
}
|
||||||
|
.landing-page .py-\[120px\] {
|
||||||
|
padding-top: 120px;
|
||||||
|
padding-bottom: 120px;
|
||||||
|
}
|
||||||
|
.landing-page .pt-\[60px\] {
|
||||||
|
padding-top: 60px;
|
||||||
|
}
|
||||||
|
.landing-page .pb-\[120px\] {
|
||||||
|
padding-bottom: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Border colors — fumadocs * { border-color: var(--color-fd-border) } persists from docs
|
/* Border colors — fumadocs * { border-color: var(--color-fd-border) } persists from docs
|
||||||
navigation. In light mode --color-fd-border = #d6d3d1 which looks bright on the dark
|
navigation. In light mode --color-fd-border = #d6d3d1 which looks bright on the dark
|
||||||
landing background, overriding explicit border-[var(--landing-border-*)] utilities. */
|
landing background, overriding explicit border-[var(--landing-border-*)] utilities. */
|
||||||
.landing-page .border-\[var\(--landing-border-subtle\)\] { border-color: var(--landing-border-subtle); }
|
.landing-page .border-\[var\(--landing-border-subtle\)\] {
|
||||||
.landing-page .border-\[var\(--landing-border-default\)\] { border-color: var(--landing-border-default); }
|
border-color: var(--landing-border-subtle);
|
||||||
.landing-page .border-\[var\(--landing-border-strong\)\] { border-color: var(--landing-border-strong); }
|
}
|
||||||
|
.landing-page .border-\[var\(--landing-border-default\)\] {
|
||||||
|
border-color: var(--landing-border-default);
|
||||||
|
}
|
||||||
|
.landing-page .border-\[var\(--landing-border-strong\)\] {
|
||||||
|
border-color: var(--landing-border-strong);
|
||||||
|
}
|
||||||
|
|
||||||
/* Font weight — fumadocs heading reset zeroes h1-h6 { font-weight: inherit } */
|
/* Font weight — fumadocs heading reset zeroes h1-h6 { font-weight: inherit } */
|
||||||
.landing-page .font-\[680\] { font-weight: 680; }
|
.landing-page .font-\[680\] {
|
||||||
|
font-weight: 680;
|
||||||
|
}
|
||||||
|
|
||||||
/* Font size (clamp values) — fumadocs heading reset zeroes h1-h6 { font-size: inherit } */
|
/* Font size (clamp values) — fumadocs heading reset zeroes h1-h6 { font-size: inherit } */
|
||||||
.landing-page .text-\[clamp\(1\.75rem\,4vw\,2\.75rem\)\] { font-size: clamp(1.75rem, 4vw, 2.75rem); }
|
.landing-page .text-\[clamp\(1\.75rem\,4vw\,2\.75rem\)\] {
|
||||||
.landing-page .text-\[clamp\(1\.375rem\,3vw\,2rem\)\] { font-size: clamp(1.375rem, 3vw, 2rem); }
|
font-size: clamp(1.75rem, 4vw, 2.75rem);
|
||||||
.landing-page .text-\[clamp\(2rem\,4vw\,3rem\)\] { font-size: clamp(2rem, 4vw, 3rem); }
|
}
|
||||||
.landing-page .text-\[clamp\(2rem\,5vw\,3\.5rem\)\] { font-size: clamp(2rem, 5vw, 3.5rem); }
|
.landing-page .text-\[clamp\(1\.375rem\,3vw\,2rem\)\] {
|
||||||
|
font-size: clamp(1.375rem, 3vw, 2rem);
|
||||||
|
}
|
||||||
|
.landing-page .text-\[clamp\(2rem\,4vw\,3rem\)\] {
|
||||||
|
font-size: clamp(2rem, 4vw, 3rem);
|
||||||
|
}
|
||||||
|
.landing-page .text-\[clamp\(2rem\,5vw\,3\.5rem\)\] {
|
||||||
|
font-size: clamp(2rem, 5vw, 3.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive md: utilities — fumadocs bundles .hidden but not all md: variants,
|
/* Responsive md: utilities — fumadocs bundles .hidden but not all md: variants,
|
||||||
so its higher-priority layer keeps elements hidden / wrong grid at desktop widths. */
|
so its higher-priority layer keeps elements hidden / wrong grid at desktop widths. */
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.landing-page .md\:flex { display: flex; }
|
.landing-page .md\:flex {
|
||||||
.landing-page .md\:block { display: block; }
|
display: flex;
|
||||||
.landing-page .md\:hidden { display: none; }
|
}
|
||||||
|
.landing-page .md\:block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.landing-page .md\:hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .md\:flex-row { flex-direction: row; }
|
.landing-page .md\:flex-row {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
.landing-page .md\:grid-cols-2 {
|
||||||
.landing-page .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
.landing-page .md\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
}
|
||||||
.landing-page .md\:grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
|
.landing-page .md\:grid-cols-3 {
|
||||||
.landing-page .md\:grid-cols-\[1fr_auto_1fr_1fr\] { grid-template-columns: 1fr auto 1fr 1fr; }
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
.landing-page .md\:grid-cols-4 {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
.landing-page .md\:grid-cols-6 {
|
||||||
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
.landing-page .md\:grid-cols-\[1fr_auto_1fr_1fr\] {
|
||||||
|
grid-template-columns: 1fr auto 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .md\:gap-0 { gap: 0; }
|
.landing-page .md\:gap-0 {
|
||||||
.landing-page .md\:gap-6 { gap: calc(var(--spacing) * 6); }
|
gap: 0;
|
||||||
.landing-page .md\:gap-12 { gap: calc(var(--spacing) * 12); }
|
}
|
||||||
|
.landing-page .md\:gap-6 {
|
||||||
|
gap: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
.landing-page .md\:gap-12 {
|
||||||
|
gap: calc(var(--spacing) * 12);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .md\:items-center { align-items: center; }
|
.landing-page .md\:items-center {
|
||||||
.landing-page .md\:items-baseline { align-items: baseline; }
|
align-items: center;
|
||||||
.landing-page .md\:items-start { align-items: flex-start; }
|
}
|
||||||
|
.landing-page .md\:items-baseline {
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
.landing-page .md\:items-start {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.landing-page .md\:order-1 { order: 1; }
|
.landing-page .md\:order-1 {
|
||||||
.landing-page .md\:order-2 { order: 2; }
|
order: 1;
|
||||||
|
}
|
||||||
|
.landing-page .md\:order-2 {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,27 +9,31 @@ touch a developer's real AO installation.
|
||||||
## Two tiers
|
## Two tiers
|
||||||
|
|
||||||
| Tier | What | Where |
|
| Tier | What | Where |
|
||||||
|------|------|-------|
|
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
||||||
| **Comprehensive (primary)** | A cross-platform Go suite that builds `ao` and exercises the full behaviour. Runs natively on **ubuntu + macOS + windows** — the only way to cover the OS-specific process-detach paths (`setsid` vs `CREATE_NEW_PROCESS_GROUP`) and `os.UserConfigDir()` resolution. | `backend/internal/cli/e2e_test.go` (build tag `e2e`) |
|
| **Comprehensive (primary)** | A cross-platform Go suite that builds `ao` and exercises the full behaviour. Runs natively on **ubuntu + macOS + windows** — the only way to cover the OS-specific process-detach paths (`setsid` vs `CREATE_NEW_PROCESS_GROUP`) and `os.UserConfigDir()` resolution. | `backend/internal/cli/e2e_test.go` (build tag `e2e`) |
|
||||||
| **Fresh-install (hardening)** | Proves a freshly installed binary works on a clean machine with no Go toolchain and no developer state. | `test/cli/Dockerfile` + `test/cli/install-check.sh` |
|
| **Fresh-install (hardening)** | Proves a freshly installed binary works on a clean machine with no Go toolchain and no developer state. | `test/cli/Dockerfile` + `test/cli/install-check.sh` |
|
||||||
|
|
||||||
## Run it
|
## Run it
|
||||||
|
|
||||||
**The Go suite (fastest, cross-platform):**
|
**The Go suite (fastest, cross-platform):**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd backend
|
cd backend
|
||||||
go test -tags e2e ./internal/cli/... # run it
|
go test -tags e2e ./internal/cli/... # run it
|
||||||
go test -tags e2e -v -run TestE2E ./internal/cli/... # verbose: prints every command + output
|
go test -tags e2e -v -run TestE2E ./internal/cli/... # verbose: prints every command + output
|
||||||
```
|
```
|
||||||
|
|
||||||
It builds its own `ao` binary; `git` must be on PATH (required by `doctor`).
|
It builds its own `ao` binary; `git` must be on PATH (required by `doctor`).
|
||||||
`-v` logs each `ao` invocation and its full output, which is the audit trail you
|
`-v` logs each `ao` invocation and its full output, which is the audit trail you
|
||||||
get for free from `go test`.
|
get for free from `go test`.
|
||||||
|
|
||||||
**Fresh-machine install, in a clean container:**
|
**Fresh-machine install, in a clean container:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build -f test/cli/Dockerfile -t ao-cli-smoke .
|
docker build -f test/cli/Dockerfile -t ao-cli-smoke .
|
||||||
docker run --rm --init ao-cli-smoke
|
docker run --rm --init ao-cli-smoke
|
||||||
```
|
```
|
||||||
|
|
||||||
> `--init` gives the container a real PID-1 reaper (tini) so the daemon the
|
> `--init` gives the container a real PID-1 reaper (tini) so the daemon the
|
||||||
> check starts is reaped after `stop` instead of lingering as a zombie.
|
> check starts is reaped after `stop` instead of lingering as a zombie.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue