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
|
||||
.DS_Store
|
||||
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.
|
||||
|
||||
**Source files to edit:**
|
||||
|
||||
- `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.
|
||||
|
||||
**Regenerate after editing:**
|
||||
|
||||
```bash
|
||||
npm run api # runs api:spec then api:ts in sequence
|
||||
```
|
||||
|
||||
This is equivalent to running:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
**Verify:**
|
||||
|
||||
```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)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ route. Run `ao <command> --help` for the authoritative flag shape; the table
|
|||
below groups what's on `main` today.
|
||||
|
||||
| Lane | Command | Purpose |
|
||||
|---|---|---|
|
||||
| ------------ | ------------------------------------ | ------------------------------------------------------------ |
|
||||
| Daemon | `ao start` | Start the daemon in the background and wait for `/readyz`. |
|
||||
| Daemon | `ao stop` | Graceful shutdown via loopback `POST /shutdown`. |
|
||||
| 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.
|
||||
|
||||
| Var | Default | Purpose |
|
||||
|---|---|---|
|
||||
| --------------------- | ------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| `AO_PORT` | `3001` | Bind port; daemon fails fast if taken. |
|
||||
| `AO_REQUEST_TIMEOUT` | `60s` | Per-request timeout (Go duration). |
|
||||
| `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
|
||||
|
||||
| Doc | What it covers |
|
||||
|-----|----------------|
|
||||
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| [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. |
|
||||
| [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.
|
||||
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
Agent adapter behavior:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ call runtime, workspace, tracker, or agent adapters in-process.
|
|||
## Current commands
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| `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 --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:
|
||||
|
||||
| Var | Default | Purpose |
|
||||
|---|---|---|
|
||||
| --------------------- | ------------------------------------------------- | ---------------------- |
|
||||
| `AO_PORT` | `3001` | Loopback daemon port. |
|
||||
| `AO_RUN_FILE` | `<UserConfigDir>/agent-orchestrator/running.json` | PID/port handshake. |
|
||||
| `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
|
||||
|
||||
| YAML field | Type | Storage today | Target |
|
||||
|---|---|---|---|
|
||||
| --------------------------------- | ---------------------- | ----------------------------------- | ---------------------------------------------------- |
|
||||
| `name` | string | `projects.display_name` | done |
|
||||
| `repo` | string | `projects.repo_origin_url` | done |
|
||||
| `path` | string | `projects.path` | done |
|
||||
|
|
@ -112,7 +112,7 @@ agent adapter.
|
|||
|
||||
## 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.
|
||||
2. **Project identity scalars** — `default_branch`, `session_prefix` (stop
|
||||
hardcoding/deriving them).
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ invariants.
|
|||
## Accepted stack
|
||||
|
||||
| Area | Decision | Status | Rationale |
|
||||
|------|----------|--------|-----------|
|
||||
| ------------------ | ----------------------------------------------------------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| Frontend shell | Electron + TypeScript | Implemented | Local desktop control plane paired with the daemon. |
|
||||
|
|
@ -71,7 +71,7 @@ config surface appears.
|
|||
## Explicitly avoided for V1
|
||||
|
||||
| Avoid | Reason |
|
||||
|-------|--------|
|
||||
| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| GORM | AO needs explicit transactional SQL and CDC-triggered writes. |
|
||||
| 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. |
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
import { notFound } from "next/navigation";
|
||||
import {
|
||||
DocsPage,
|
||||
DocsBody,
|
||||
DocsTitle,
|
||||
DocsDescription,
|
||||
} from "fumadocs-ui/page";
|
||||
import { DocsPage, DocsBody, DocsTitle, DocsDescription } from "fumadocs-ui/page";
|
||||
import type { Metadata } from "next";
|
||||
import { source } from "@/lib/source";
|
||||
import { getMDXComponents } from "@/components/docs/mdx-components";
|
||||
|
|
@ -55,9 +50,7 @@ export async function generateStaticParams() {
|
|||
return source.generateParams();
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: PageProps): Promise<Metadata> {
|
||||
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const page = source.getPage(slug);
|
||||
if (!page) {
|
||||
|
|
|
|||
|
|
@ -199,11 +199,7 @@
|
|||
#nd-docs-layout code,
|
||||
#nd-docs-layout kbd,
|
||||
#nd-docs-layout pre {
|
||||
font-family:
|
||||
var(--font-jetbrains-mono),
|
||||
ui-monospace,
|
||||
"SFMono-Regular",
|
||||
monospace;
|
||||
font-family: var(--font-jetbrains-mono), ui-monospace, "SFMono-Regular", monospace;
|
||||
}
|
||||
|
||||
/* Sidebar — smaller text like dashboard */
|
||||
|
|
@ -512,7 +508,10 @@ pre.shiki code {
|
|||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -51,10 +51,9 @@ const links: LinkItemType[] = [
|
|||
async function GitHubStars() {
|
||||
let stars: string | null = null;
|
||||
try {
|
||||
const res = await fetch(
|
||||
"https://api.github.com/repos/ComposioHQ/agent-orchestrator",
|
||||
{ next: { revalidate: 3600 } },
|
||||
);
|
||||
const res = await fetch("https://api.github.com/repos/ComposioHQ/agent-orchestrator", {
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const count = data.stargazers_count as number;
|
||||
|
|
@ -66,14 +65,7 @@ async function GitHubStars() {
|
|||
|
||||
return stars ? (
|
||||
<span className="inline-flex items-center gap-1 text-[var(--color-text-muted)]">
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<svg 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" />
|
||||
</svg>
|
||||
{stars}
|
||||
|
|
@ -83,24 +75,14 @@ async function GitHubStars() {
|
|||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<RootProvider
|
||||
theme={{ enabled: true, defaultTheme: "dark" }}
|
||||
search={{ options: { type: "static" } }}
|
||||
>
|
||||
<RootProvider theme={{ enabled: true, defaultTheme: "dark" }} search={{ options: { type: "static" } }}>
|
||||
<DocsLayout
|
||||
tree={source.pageTree}
|
||||
links={links}
|
||||
nav={{
|
||||
title: (
|
||||
<span className="flex items-center gap-2 font-semibold">
|
||||
<img
|
||||
src="/ao-logo.svg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
width={22}
|
||||
height={22}
|
||||
className="h-[22px] w-[22px]"
|
||||
/>
|
||||
<img 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>
|
||||
),
|
||||
|
|
|
|||
|
|
@ -27,13 +27,19 @@ export default async function LandingPage() {
|
|||
<LandingAbout />
|
||||
<LandingAgentsBar />
|
||||
<LandingFeatures />
|
||||
<div id="workflow"><LandingWorkflow /></div>
|
||||
<div id="usecases"><LandingUseCases /></div>
|
||||
<div id="workflow">
|
||||
<LandingWorkflow />
|
||||
</div>
|
||||
<div id="usecases">
|
||||
<LandingUseCases />
|
||||
</div>
|
||||
<LandingHowItWorks />
|
||||
<LandingVideo />
|
||||
<LandingStats stats={githubStats} />
|
||||
<LandingTestimonials />
|
||||
<div id="quickstart"><LandingQuickStart /></div>
|
||||
<div id="quickstart">
|
||||
<LandingQuickStart />
|
||||
</div>
|
||||
<LandingCTA />
|
||||
<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
|
||||
|
|
|
|||
|
|
@ -14,10 +14,16 @@ export function LandingAbout() {
|
|||
|
||||
<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]">
|
||||
Agent Orchestrator replaces that with one YAML file. Point it at
|
||||
your GitHub issues, pick your agents, and walk away. Each agent
|
||||
spawns in its own git worktree, creates PRs, fixes CI failures,
|
||||
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>.
|
||||
Agent Orchestrator replaces that with one YAML file. Point it at your GitHub issues, pick your agents, and
|
||||
walk away. Each agent spawns in its own git worktree, creates PRs, fixes CI failures, 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>
|
||||
.
|
||||
</p>
|
||||
|
||||
{/* 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">
|
||||
{agents.map((agent) => (
|
||||
<div key={agent.name} className="flex flex-col items-center gap-2">
|
||||
<img
|
||||
src={agent.src}
|
||||
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>
|
||||
<img src={agent.src} 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>
|
||||
|
|
|
|||
|
|
@ -15,13 +15,11 @@ export function LandingDifferentiators() {
|
|||
Why Agent Orchestrator
|
||||
</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]">
|
||||
The only{" "}
|
||||
<em className="italic text-[var(--landing-muted)]">open-source, web-based</em>{" "}
|
||||
agent orchestrator
|
||||
The only <em className="italic text-[var(--landing-muted)]">open-source, web-based</em> agent orchestrator
|
||||
</h2>
|
||||
<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
|
||||
your browser, works on any OS, and you can self-host or extend it.
|
||||
Conductor, T3 Code, and Codex App are native Mac apps. AO runs in your browser, works on any OS, and you can
|
||||
self-host or extend it.
|
||||
</p>
|
||||
</div>
|
||||
<div className="landing-reveal landing-card rounded-2xl overflow-hidden">
|
||||
|
|
@ -45,12 +43,8 @@ export function LandingDifferentiators() {
|
|||
key={row.feature}
|
||||
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">
|
||||
{row.feature}
|
||||
</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-[0.8125rem] text-[var(--landing-fg)]/80">{row.feature}</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">
|
||||
{row.others}
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -145,21 +145,15 @@ export function LandingFeatures() {
|
|||
marginBottom: "1.5rem",
|
||||
transformOrigin: "center top",
|
||||
transition: "transform 0.4s ease, opacity 0.4s ease, border-color 0.2s ease",
|
||||
...(stack
|
||||
? { position: "sticky", top: `${BASE_TOP + i * STACK_GAP}px`, zIndex: i + 1 }
|
||||
: null),
|
||||
...(stack ? { position: "sticky", top: `${BASE_TOP + i * STACK_GAP}px`, zIndex: i + 1 } : null),
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div className="font-mono text-xs tracking-[0.1em] text-[var(--landing-muted)] opacity-50 mb-4">
|
||||
{f.n}
|
||||
</div>
|
||||
<h3 className="font-sans font-[680] tracking-tight text-[1.375rem] mb-4">
|
||||
{f.title}
|
||||
</h3>
|
||||
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.7] max-w-[28rem]">
|
||||
{f.desc}
|
||||
</p>
|
||||
<h3 className="font-sans font-[680] tracking-tight text-[1.375rem] mb-4">{f.title}</h3>
|
||||
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.7] max-w-[28rem]">{f.desc}</p>
|
||||
</div>
|
||||
<FeatureDemo kind={f.demo} />
|
||||
</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"
|
||||
>
|
||||
<div className="flex items-center gap-1.5 mb-1">
|
||||
<span
|
||||
className="w-1.5 h-1.5 rounded-full shrink-0"
|
||||
style={{ background: a.color }}
|
||||
/>
|
||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/85 truncate">
|
||||
{a.name}
|
||||
</span>
|
||||
<span className="w-1.5 h-1.5 rounded-full shrink-0" style={{ background: a.color }} />
|
||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/85 truncate">{a.name}</span>
|
||||
</div>
|
||||
<div className="font-mono text-[0.625rem] text-[var(--landing-muted)] opacity-65 mb-auto truncate">
|
||||
{a.task}
|
||||
|
|
@ -243,13 +232,8 @@ function ParallelFront() {
|
|||
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"
|
||||
>
|
||||
<span
|
||||
className="w-1.5 h-1.5 rounded-full shrink-0"
|
||||
style={{ background: a.color }}
|
||||
/>
|
||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/85 truncate">
|
||||
{a.name}
|
||||
</span>
|
||||
<span className="w-1.5 h-1.5 rounded-full shrink-0" style={{ background: a.color }} />
|
||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/85 truncate">{a.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -284,9 +268,7 @@ function RecoveryBack() {
|
|||
<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)]">
|
||||
<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)]">
|
||||
healing
|
||||
</span>
|
||||
<span className="text-[0.5625rem] uppercase tracking-[0.1em] text-[var(--landing-muted-dim)]">healing</span>
|
||||
</div>
|
||||
<div className="flex-1 space-y-1.5 overflow-hidden">
|
||||
{visible.map((s, i) => {
|
||||
|
|
@ -304,9 +286,7 @@ function RecoveryBack() {
|
|||
key={`${i}-${s.text}`}
|
||||
className={`flex items-baseline gap-2.5 ${isLast ? "landing-stream-line" : ""}`}
|
||||
>
|
||||
<span className="text-[var(--landing-muted-dim)] opacity-50 w-9 shrink-0">
|
||||
{s.time}
|
||||
</span>
|
||||
<span className="text-[var(--landing-muted-dim)] opacity-50 w-9 shrink-0">{s.time}</span>
|
||||
<span className={`${color} truncate`}>{s.text}</span>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -320,16 +300,12 @@ function RecoveryFront() {
|
|||
return (
|
||||
<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">
|
||||
<span className="font-mono text-[0.5rem] tracking-[0.12em] uppercase text-[rgba(248,113,113,0.7)]">
|
||||
before
|
||||
</span>
|
||||
<span className="font-mono text-[0.5rem] tracking-[0.12em] uppercase text-[rgba(248,113,113,0.7)]">before</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>
|
||||
</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">
|
||||
<span className="font-mono text-[0.5rem] tracking-[0.12em] uppercase text-[rgba(134,239,172,0.7)]">
|
||||
after
|
||||
</span>
|
||||
<span className="font-mono text-[0.5rem] tracking-[0.12em] uppercase text-[rgba(134,239,172,0.7)]">after</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>
|
||||
</div>
|
||||
|
|
@ -357,9 +333,7 @@ function PluginsBack() {
|
|||
return (
|
||||
<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)]">
|
||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/80">
|
||||
agent-orchestrator.yaml
|
||||
</span>
|
||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/80">agent-orchestrator.yaml</span>
|
||||
<span className="font-mono text-[0.5625rem] tracking-[0.1em] uppercase text-[var(--landing-muted-dim)]">
|
||||
7 slots
|
||||
</span>
|
||||
|
|
@ -369,9 +343,7 @@ function PluginsBack() {
|
|||
const val = s.values[(tick + i) % s.values.length];
|
||||
return (
|
||||
<div key={s.slot} className="flex items-center gap-3">
|
||||
<span className="text-[var(--landing-muted-dim)] w-[4.5rem] shrink-0">
|
||||
{s.slot}:
|
||||
</span>
|
||||
<span className="text-[var(--landing-muted-dim)] w-[4.5rem] shrink-0">{s.slot}:</span>
|
||||
<span
|
||||
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)]"
|
||||
|
|
@ -448,9 +420,7 @@ function DashboardBack() {
|
|||
return prev.map((c) => ({ ...c, col: 0 as 0 | 1 | 2 }));
|
||||
}
|
||||
const oldest = advanceable[0];
|
||||
return prev.map((c) =>
|
||||
c.id === oldest.id ? { ...c, col: (c.col + 1) as 0 | 1 | 2 } : c,
|
||||
);
|
||||
return prev.map((c) => (c.id === oldest.id ? { ...c, col: (c.col + 1) as 0 | 1 | 2 } : c));
|
||||
});
|
||||
}, 2400);
|
||||
return () => clearInterval(id);
|
||||
|
|
@ -459,9 +429,7 @@ function DashboardBack() {
|
|||
return (
|
||||
<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)]">
|
||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/80">
|
||||
my-saas-app · 4 sessions
|
||||
</span>
|
||||
<span className="font-mono text-[0.6875rem] text-[var(--landing-fg)]/80">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="w-1 h-1 rounded-full bg-[rgba(134,239,172,0.7)] landing-sse-pulse" />
|
||||
sse
|
||||
|
|
@ -482,13 +450,8 @@ function DashboardBack() {
|
|||
>
|
||||
<div className="text-[var(--landing-fg)]/85 truncate mb-1">{c.title}</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span
|
||||
className="w-1 h-1 rounded-full shrink-0"
|
||||
style={{ background: c.color }}
|
||||
/>
|
||||
<span className="font-mono text-[0.5rem] text-[var(--landing-muted-dim)] truncate">
|
||||
{c.agent}
|
||||
</span>
|
||||
<span className="w-1 h-1 rounded-full shrink-0" style={{ background: c.color }} />
|
||||
<span className="font-mono text-[0.5rem] text-[var(--landing-muted-dim)] truncate">{c.agent}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -546,12 +509,9 @@ function DashboardFront() {
|
|||
{stream.map((l) => (
|
||||
<div
|
||||
key={l.id}
|
||||
className={`truncate transition-opacity duration-200 ${
|
||||
l.exiting ? "opacity-0" : "landing-stream-line"
|
||||
}`}
|
||||
className={`truncate transition-opacity duration-200 ${l.exiting ? "opacity-0" : "landing-stream-line"}`}
|
||||
>
|
||||
<span className="text-[rgba(134,239,172,0.7)]">✓</span>{" "}
|
||||
<span className="opacity-70">{l.text}</span>
|
||||
<span className="text-[rgba(134,239,172,0.7)]">✓</span> <span className="opacity-70">{l.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@ export function LandingHero({ starsLabel }: LandingHeroProps) {
|
|||
<span className="text-[var(--landing-muted)]">One dashboard.</span>
|
||||
</h1>
|
||||
<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
|
||||
in isolated git worktrees. Each agent gets its own branch, creates PRs,
|
||||
fixes CI, and addresses reviews autonomously.
|
||||
Agent Orchestrator spawns Claude Code, Codex, Cursor, Aider, and OpenCode in isolated git worktrees. Each
|
||||
agent gets its own branch, creates PRs, fixes CI, and addresses reviews autonomously.
|
||||
</p>
|
||||
<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">
|
||||
|
|
|
|||
|
|
@ -78,12 +78,9 @@ export function LandingHowItWorks() {
|
|||
return (
|
||||
<section className="py-[120px] px-6 max-w-[72rem] mx-auto" id="how">
|
||||
<div className="landing-reveal">
|
||||
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-60 mb-6">
|
||||
Process
|
||||
</div>
|
||||
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-60 mb-6">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">
|
||||
Three steps to{" "}
|
||||
<em className="italic text-[var(--landing-muted)]">orchestration</em>
|
||||
Three steps to <em className="italic text-[var(--landing-muted)]">orchestration</em>
|
||||
</h2>
|
||||
</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"
|
||||
style={{
|
||||
flex: isDesktop
|
||||
? isActive
|
||||
? "1 1 0%"
|
||||
: "0 1 15rem"
|
||||
: "0 0 auto",
|
||||
flex: isDesktop ? (isActive ? "1 1 0%" : "0 1 15rem") : "0 0 auto",
|
||||
transition: "flex 0.6s cubic-bezier(0.22,1,0.36,1)",
|
||||
}}
|
||||
>
|
||||
|
|
@ -132,17 +125,13 @@ export function LandingHowItWorks() {
|
|||
<h3
|
||||
className="font-sans font-[680] tracking-tight text-[1.375rem] leading-[1.15]"
|
||||
style={{
|
||||
color: isActive
|
||||
? "var(--landing-fg)"
|
||||
: "var(--landing-muted)",
|
||||
color: isActive ? "var(--landing-fg)" : "var(--landing-muted)",
|
||||
transition: "color 0.4s ease",
|
||||
maxWidth: isActive ? "100%" : "11rem",
|
||||
}}
|
||||
>
|
||||
{step.title.replace(` ${step.titleEm}`, "")}{" "}
|
||||
<em className="italic text-[var(--landing-muted)]">
|
||||
{step.titleEm}
|
||||
</em>
|
||||
<em className="italic text-[var(--landing-muted)]">{step.titleEm}</em>
|
||||
</h3>
|
||||
|
||||
{/* Expanding body */}
|
||||
|
|
@ -225,7 +214,9 @@ function CliDemo() {
|
|||
<div className="text-[var(--landing-muted)] opacity-60"> </div>
|
||||
<div>
|
||||
<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>
|
||||
|
|
@ -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>
|
||||
</div>
|
||||
<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: "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="Review" cards={[
|
||||
{ title: "Update API tests", meta: "#45 · PR #310", agent: "claude-code", amber: true },
|
||||
]} />
|
||||
<DashColumn title="Merged" cards={[
|
||||
{ title: "Refactor DB layer", meta: "#46 · PR #308", agent: "opencode", done: true },
|
||||
]} />
|
||||
]}
|
||||
/>
|
||||
<DashColumn title="Pending" cards={[{ title: "Add rate limiting", meta: "#44 · PR #312", agent: "aider" }]} />
|
||||
<DashColumn
|
||||
title="Review"
|
||||
cards={[{ title: "Update API tests", meta: "#45 · PR #310", agent: "claude-code", amber: true }]}
|
||||
/>
|
||||
<DashColumn
|
||||
title="Merged"
|
||||
cards={[{ title: "Refactor DB layer", meta: "#46 · PR #308", agent: "opencode", done: true }]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -269,7 +263,10 @@ function PrsDemo() {
|
|||
{ branch: "feat/rate-limiting", title: "Add Redis-backed rate limiter" },
|
||||
{ branch: "refactor/db-layer", title: "Extract repository pattern from services" },
|
||||
].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="font-mono text-xs text-[var(--landing-fg)]/70">{pr.branch}</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}
|
||||
</div>
|
||||
{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="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">
|
||||
{card.done ? (
|
||||
<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}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,17 +36,26 @@ export function LandingNav() {
|
|||
</a>
|
||||
<ul className="hidden md:flex items-center gap-8 list-none">
|
||||
<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
|
||||
</a>
|
||||
</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
|
||||
</a>
|
||||
</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
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
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" },
|
||||
];
|
||||
|
||||
|
|
@ -12,8 +22,7 @@ export function LandingQuickStart() {
|
|||
Get started in 60 seconds
|
||||
</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">
|
||||
Three commands to{" "}
|
||||
<em className="italic text-[var(--landing-muted)]">launch</em>
|
||||
Three commands to <em className="italic text-[var(--landing-muted)]">launch</em>
|
||||
</h2>
|
||||
</div>
|
||||
<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">
|
||||
{s.num}
|
||||
</div>
|
||||
<h3 className="font-sans font-[680] tracking-tight text-xl mb-2 tracking-tight">
|
||||
{s.title}
|
||||
</h3>
|
||||
<p className="text-[var(--landing-muted)] text-[0.8125rem] leading-[1.6] mb-4">
|
||||
{s.desc}
|
||||
</p>
|
||||
<h3 className="font-sans font-[680] tracking-tight text-xl mb-2 tracking-tight">{s.title}</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">
|
||||
<span className="text-[var(--landing-muted)] opacity-40">$</span> {s.cmd}
|
||||
</div>
|
||||
|
|
@ -35,7 +40,10 @@ export function LandingQuickStart() {
|
|||
))}
|
||||
</div>
|
||||
<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
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,16 +16,11 @@ export function LandingStats({ stats }: LandingStatsProps) {
|
|||
<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">
|
||||
{cards.map((stat) => (
|
||||
<div
|
||||
key={stat.label}
|
||||
className="landing-card rounded-2xl py-8 px-6 text-center"
|
||||
>
|
||||
<div 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">
|
||||
{stat.number}
|
||||
</div>
|
||||
<div className="text-xs text-[var(--landing-muted)] opacity-60">
|
||||
{stat.label}
|
||||
</div>
|
||||
<div className="text-xs text-[var(--landing-muted)] opacity-60">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import { useEffect, useState } from "react";
|
|||
|
||||
const testimonials = [
|
||||
{
|
||||
quote:
|
||||
"Set up 12 agents on our backlog before lunch. By end of day, 8 PRs were merged.",
|
||||
quote: "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",
|
||||
name: "Staff Engineer",
|
||||
role: "Series B Startup",
|
||||
|
|
@ -44,10 +43,7 @@ export function LandingTestimonials() {
|
|||
|
||||
useEffect(() => {
|
||||
if (paused) return;
|
||||
const t = window.setTimeout(
|
||||
() => change((active + 1) % testimonials.length),
|
||||
ROTATE_MS,
|
||||
);
|
||||
const t = window.setTimeout(() => change((active + 1) % testimonials.length), ROTATE_MS);
|
||||
return () => window.clearTimeout(t);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [active, paused]);
|
||||
|
|
@ -65,11 +61,7 @@ export function LandingTestimonials() {
|
|||
</h2>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="landing-reveal mt-16"
|
||||
onMouseEnter={() => setPaused(true)}
|
||||
onMouseLeave={() => setPaused(false)}
|
||||
>
|
||||
<div className="landing-reveal mt-16" onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}>
|
||||
{/* Quote — fades on change */}
|
||||
<div className="min-h-[8rem] max-w-[58rem]">
|
||||
<blockquote
|
||||
|
|
@ -128,10 +120,7 @@ export function LandingTestimonials() {
|
|||
</div>
|
||||
|
||||
{/* Vertical divider */}
|
||||
<div
|
||||
className="w-px h-10 shrink-0"
|
||||
style={{ background: "var(--landing-border-default)" }}
|
||||
/>
|
||||
<div className="w-px h-10 shrink-0" style={{ background: "var(--landing-border-default)" }} />
|
||||
|
||||
{/* Author — fades on change */}
|
||||
<div
|
||||
|
|
@ -140,12 +129,8 @@ export function LandingTestimonials() {
|
|||
transition: "opacity 0.4s ease 0.05s",
|
||||
}}
|
||||
>
|
||||
<div className="text-[0.9375rem] font-medium text-[var(--landing-fg)]">
|
||||
{t.name}
|
||||
</div>
|
||||
<div className="text-[0.8125rem] text-[var(--landing-muted)] opacity-60">
|
||||
{t.role}
|
||||
</div>
|
||||
<div className="text-[0.9375rem] font-medium text-[var(--landing-fg)]">{t.name}</div>
|
||||
<div className="text-[0.8125rem] text-[var(--landing-muted)] opacity-60">{t.role}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -135,8 +135,7 @@ export function LandingUseCases() {
|
|||
One orchestrator, many jobs
|
||||
</h2>
|
||||
<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
|
||||
run can do.
|
||||
Point AO at the work and walk away — drag to explore what a single run can do.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -157,10 +156,8 @@ export function LandingUseCases() {
|
|||
maxWidth: "1120px",
|
||||
cursor: "grab",
|
||||
touchAction: "pan-y",
|
||||
WebkitMaskImage:
|
||||
"linear-gradient(to right, transparent, #000 16%, #000 84%, transparent)",
|
||||
maskImage:
|
||||
"linear-gradient(to right, transparent, #000 16%, #000 84%, transparent)",
|
||||
WebkitMaskImage: "linear-gradient(to right, transparent, #000 16%, #000 84%, transparent)",
|
||||
maskImage: "linear-gradient(to right, transparent, #000 16%, #000 84%, transparent)",
|
||||
}}
|
||||
>
|
||||
<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">
|
||||
{c.eyebrow}
|
||||
</div>
|
||||
<h3
|
||||
className="font-sans font-[680] text-[1.3125rem] tracking-tight"
|
||||
style={{ marginTop: "1rem" }}
|
||||
>
|
||||
<h3 className="font-sans font-[680] text-[1.3125rem] tracking-tight" style={{ marginTop: "1rem" }}>
|
||||
{c.title}
|
||||
</h3>
|
||||
<p
|
||||
|
|
@ -218,12 +212,9 @@ export function LandingUseCases() {
|
|||
style={{ marginTop: "auto", padding: "0.75rem 0.875rem" }}
|
||||
>
|
||||
<div className="whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<span className={dim}>{c.prefix}</span>{" "}
|
||||
<span className={fg}>{c.cmd}</span>
|
||||
</div>
|
||||
<div className={`whitespace-nowrap overflow-hidden text-ellipsis ${ok}`}>
|
||||
→ {c.outcome}
|
||||
<span className={dim}>{c.prefix}</span> <span className={fg}>{c.cmd}</span>
|
||||
</div>
|
||||
<div className={`whitespace-nowrap overflow-hidden text-ellipsis ${ok}`}>→ {c.outcome}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,12 +14,42 @@ type 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: "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." },
|
||||
{
|
||||
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: "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)
|
||||
|
|
@ -58,10 +88,7 @@ export function LandingWorkflow() {
|
|||
useEffect(() => {
|
||||
const el = wrapRef.current;
|
||||
if (!el) return;
|
||||
const ob = new IntersectionObserver(
|
||||
([entry]) => entry.isIntersecting && setInView(true),
|
||||
{ threshold: 0.25 },
|
||||
);
|
||||
const ob = new IntersectionObserver(([entry]) => entry.isIntersecting && setInView(true), { threshold: 0.25 });
|
||||
ob.observe(el);
|
||||
return () => ob.disconnect();
|
||||
}, []);
|
||||
|
|
@ -96,10 +123,7 @@ export function LandingWorkflow() {
|
|||
// Auto-loop
|
||||
useEffect(() => {
|
||||
if (!inView || paused) return;
|
||||
const t = window.setTimeout(
|
||||
() => setActive((a) => (a + 1) % milestones.length),
|
||||
STEP_MS,
|
||||
);
|
||||
const t = window.setTimeout(() => setActive((a) => (a + 1) % milestones.length), STEP_MS);
|
||||
return () => window.clearTimeout(t);
|
||||
}, [active, paused, inView]);
|
||||
|
||||
|
|
@ -189,9 +213,7 @@ export function LandingWorkflow() {
|
|||
style={{ opacity: show ? 1 : 0, transition: "opacity 0.35s ease" }}
|
||||
>
|
||||
<LifecycleIcon kind={cur.icon} />
|
||||
<div className="font-sans font-[680] tracking-tight text-[1.5rem] tracking-[-0.5px] mt-3">
|
||||
{cur.label}
|
||||
</div>
|
||||
<div className="font-sans font-[680] tracking-tight text-[1.5rem] tracking-[-0.5px] mt-3">{cur.label}</div>
|
||||
<div className="font-mono text-[0.6875rem] tracking-[0.08em] text-[var(--landing-accent)] opacity-80 mt-1">
|
||||
{cur.key}
|
||||
</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">
|
||||
{/* 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)" />
|
||||
|
||||
{/* ticks */}
|
||||
|
|
@ -257,7 +287,6 @@ export function LandingWorkflow() {
|
|||
>
|
||||
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.6]">{cur.desc}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -145,11 +145,6 @@ export function PageConstellation() {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="fixed inset-0 pointer-events-none"
|
||||
style={{ zIndex: 0 }}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<canvas 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) => {
|
||||
|
|
|
|||
|
|
@ -19,29 +19,19 @@ export function DocsMissingPage() {
|
|||
<DocsBody>
|
||||
<div className="not-prose docs-missing-wrap">
|
||||
<div className="docs-missing-card">
|
||||
<div className="docs-missing-label">
|
||||
docs / checkout failed
|
||||
</div>
|
||||
<div className="docs-missing-label">docs / checkout failed</div>
|
||||
<div className="docs-missing-content">
|
||||
<section className="docs-missing-copy">
|
||||
<h2>
|
||||
This page checked out the wrong worktree.
|
||||
</h2>
|
||||
<h2>This page checked out the wrong worktree.</h2>
|
||||
<p>
|
||||
The docs were rebuilt, and this URL did not survive the merge. Start from the docs
|
||||
index, or use search in the sidebar to find where it landed.
|
||||
The docs were rebuilt, and this URL did not survive the merge. Start from the docs index, or use
|
||||
search in the sidebar to find where it landed.
|
||||
</p>
|
||||
<div className="docs-missing-actions">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="docs-missing-primary"
|
||||
>
|
||||
<Link href="/docs" className="docs-missing-primary">
|
||||
Browse docs
|
||||
</Link>
|
||||
<Link
|
||||
href="/"
|
||||
className="docs-missing-secondary"
|
||||
>
|
||||
<Link href="/" className="docs-missing-secondary">
|
||||
Home
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -45,9 +45,7 @@ function Cell({ platform, status }: { platform: "macos" | "linux" | "windows"; s
|
|||
>
|
||||
<Logo name={logoName} size={18} />
|
||||
<div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
|
||||
<span style={{ fontSize: "0.8125rem", fontWeight: 600, color: "var(--color-fd-foreground)" }}>
|
||||
{title}
|
||||
</span>
|
||||
<span style={{ fontSize: "0.8125rem", fontWeight: 600, color: "var(--color-fd-foreground)" }}>{title}</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "0.75rem",
|
||||
|
|
@ -73,12 +71,7 @@ function Cell({ platform, status }: { platform: "macos" | "linux" | "windows"; s
|
|||
);
|
||||
}
|
||||
|
||||
export function PlatformSupport({
|
||||
macos = "full",
|
||||
linux = "full",
|
||||
windows = "full",
|
||||
note,
|
||||
}: PlatformSupportProps) {
|
||||
export function PlatformSupport({ macos = "full", linux = "full", windows = "full", note }: PlatformSupportProps) {
|
||||
return (
|
||||
<div style={{ margin: "1.25rem 0" }}>
|
||||
<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.
|
||||
|
||||
| Slot | Default | Purpose | Interface |
|
||||
|------|---------|---------|-----------|
|
||||
| --------- | ----------------------------------------------- | ---------------------------------------------------------------- | ------------------ |
|
||||
| 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` |
|
||||
| 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` |
|
||||
|
||||
<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>
|
||||
|
||||
---
|
||||
|
|
@ -57,7 +59,7 @@ pr_open ────────────────────────
|
|||
Terminal statuses (session is dead and will no longer be polled): `killed`, `terminated`, `done`, `cleanup`, `errored`, `merged`.
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| ------------------- | ----------------------------------------------------------------------------- |
|
||||
| `spawning` | Session is being created — worktree, branch, and tmux window are initialising |
|
||||
| `working` | Agent is active; no PR yet |
|
||||
| `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
|
||||
|
||||
| `event.type` | Priority | When emitted |
|
||||
|---|---|---|
|
||||
| ---------------------------- | -------- | -------------------------------------------------- |
|
||||
| `session.spawned` | info | Session transitions out of `spawning` |
|
||||
| `session.working` | info | Session enters `working` |
|
||||
| `session.exited` | info | Agent process exits |
|
||||
|
|
@ -205,7 +207,7 @@ Every agent plugin must implement `getActivityState(session, readyThresholdMs?)`
|
|||
### The 6 activity states
|
||||
|
||||
| State | Meaning | When |
|
||||
|---|---|---|
|
||||
| --------------- | ----------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| `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 |
|
||||
| `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">
|
||||
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>
|
||||
|
||||
### Two JSONL patterns
|
||||
|
||||
| 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. |
|
||||
| **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
|
||||
|
||||
| Constant | Value | Purpose |
|
||||
|---|---|---|
|
||||
| ----------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `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` |
|
||||
| `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).
|
||||
|
||||
The wrappers intercept:
|
||||
|
||||
- `gh pr create` — captures the PR URL from stdout and writes `pr=<url>` and `status=pr_open`
|
||||
- `gh pr merge` — writes `status=merged`
|
||||
- `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
|
||||
|
||||
<Cards>
|
||||
<Card title="Project Configuration" href="/docs/configuration/projects" 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." />
|
||||
<Card
|
||||
title="Project Configuration"
|
||||
href="/docs/configuration/projects"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ ao
|
|||
> Start orchestrator agent and dashboard (auto-creates config on first run, adds projects by path/URL)
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ------------------- | ------- | ------------------------------------------------------------------------------- |
|
||||
| `--no-dashboard` | — | Skip starting the dashboard server |
|
||||
| `--no-orchestrator` | — | Skip starting the orchestrator agent |
|
||||
| `--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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ----------------- | ------- | ------------------------------------------------ |
|
||||
| `--purge-session` | — | Delete the mapped OpenCode session when stopping |
|
||||
| `--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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ---------------------- | ------- | --------------------- |
|
||||
| `-p, --project <id>` | — | Filter to one project |
|
||||
| `--json` | — | Output JSON |
|
||||
| `-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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| -------------------- | -------------- | --------------------------------------------------------------------------------- |
|
||||
| `--open` | — | Open the session in a terminal tab |
|
||||
| `--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 |
|
||||
|
|
@ -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`.
|
||||
|
||||
<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>
|
||||
|
||||
### `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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| -------- | ------- | ------------------------------ |
|
||||
| `--open` | — | Open sessions in terminal tabs |
|
||||
|
||||
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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| --------------------- | ------- | -------------------------------------------------------- |
|
||||
| `-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 |
|
||||
| `--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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ----------- | ------- | ------------------------------------------ |
|
||||
| `--dry-run` | — | Show what would happen; don't nudge agents |
|
||||
|
||||
Checks all projects if `[project]` is omitted.
|
||||
|
|
@ -126,7 +127,7 @@ Checks all projects if `[project]` is omitted.
|
|||
> Start the web dashboard
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ------------------- | ----------------------- | ----------------------------------------- |
|
||||
| `-p, --port <port>` | config `port` or `3000` | Port to listen on |
|
||||
| `--no-open` | — | Don't open the browser |
|
||||
| `--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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ------------------ | ------- | ---------------------------------------------- |
|
||||
| `-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).
|
||||
|
|
@ -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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| --------------------- | ------- | ------------------------------------------------ |
|
||||
| `-p, --project <id>` | — | Required if multiple projects |
|
||||
| `--fail` | — | Mark as failed instead of passing |
|
||||
| `-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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| --------------- | ------- | --------------------------------------------------------- |
|
||||
| `--fix` | — | Apply safe fixes for launcher and stale-temp issues |
|
||||
| `--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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| -------------- | ------- | ------------------------------------------------------------------ |
|
||||
| `--skip-smoke` | — | Skip smoke tests after 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 |
|
||||
|
|
@ -192,7 +193,7 @@ No flags.
|
|||
> Send a manual demo notification without spawning sessions
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| -------------------- | ------- | ------------------------------------------------------------- |
|
||||
| `--template <name>` | `basic` | Demo template to send |
|
||||
| `--to <refs>` | — | Comma-separated notifier refs to target |
|
||||
| `--all` | — | Send to all configured, default, and routed notifier refs |
|
||||
|
|
@ -210,7 +211,7 @@ No flags.
|
|||
> List all sessions
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| -------------------- | ------- | ----------------------------- |
|
||||
| `-p, --project <id>` | — | Filter to one project |
|
||||
| `-a, --all` | — | Include orchestrator sessions |
|
||||
| `--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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ----------------- | ------- | ---------------------------------- |
|
||||
| `--purge-session` | — | Delete the mapped OpenCode session |
|
||||
|
||||
### `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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| -------------------- | ------- | ----------------------------- |
|
||||
| `-p, --project <id>` | — | Filter to one project |
|
||||
| `--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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| -------------------- | ------- | ------------------------------------------------- |
|
||||
| `--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.
|
||||
|
|
@ -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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ------------- | ------- | ------------------------ |
|
||||
| `-f, --force` | — | Override a stale mapping |
|
||||
|
||||
## Setup subcommands
|
||||
|
|
@ -270,7 +271,7 @@ mode, AO asks which notification priorities the notifier should receive before
|
|||
writing `notificationRouting`.
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| ------------------------------- | ------------------------------------------------------ |
|
||||
| `ao setup dashboard` | Configure dashboard notification retention and routing |
|
||||
| `ao setup desktop` | Configure native desktop notifications |
|
||||
| `ao setup webhook` | Configure a generic HTTP webhook |
|
||||
|
|
@ -288,7 +289,7 @@ writing `notificationRouting`.
|
|||
> Connect AO notifications to an OpenClaw gateway
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ------------------------------- | --------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| `--url <url>` | — | OpenClaw webhook URL |
|
||||
| `--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` |
|
||||
|
|
@ -308,7 +309,7 @@ Interactive mode (TTY) uses `@clack/prompts` with review/change steps for the ga
|
|||
> List bundled marketplace plugins
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| --------------- | ------- | ----------------------------------------------------------------------------------------- |
|
||||
| `--installed` | — | Only show plugins present in your current config |
|
||||
| `--type <slot>` | — | Filter by slot: `agent`, `runtime`, `tracker`, `scm`, `notifier`, `workspace`, `terminal` |
|
||||
| `--refresh` | — | Re-fetch the marketplace catalog |
|
||||
|
|
@ -324,7 +325,7 @@ No flags.
|
|||
> Scaffold a new AO plugin package
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ----------------------- | ------- | ------------------------------- |
|
||||
| `--name <name>` | prompt | Plugin name |
|
||||
| `--slot <slot>` | prompt | Which slot it implements |
|
||||
| `--description <text>` | prompt | Manifest description |
|
||||
|
|
@ -337,7 +338,7 @@ No flags.
|
|||
> Install a plugin into the current config
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ------------------------------- | --------------------------- | ----------------------------------------------------------------- |
|
||||
| `--url <url>` | — | Passed to `ao setup openclaw` when installing `notifier-openclaw` |
|
||||
| `--token <token>` | — | Remote/manual OpenClaw token fallback |
|
||||
| `--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
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|---|---|---|
|
||||
| ------- | ------- | -------------------------- |
|
||||
| `--all` | — | Update all managed plugins |
|
||||
|
||||
Requires either `[reference]` or `--all`.
|
||||
|
|
@ -365,7 +366,7 @@ No flags.
|
|||
A few env vars affect every command:
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| ----------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `AO_DATA_DIR` | Override the `~/.agent-orchestrator` base directory. |
|
||||
| `AO_LOG_LEVEL` | `error` · `warn` · `info` · `debug` · `trace` |
|
||||
| `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.
|
||||
|
||||
<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>
|
||||
|
||||
## What AO Creates
|
||||
|
|
@ -73,7 +75,7 @@ agentRules: |
|
|||
## What To Edit
|
||||
|
||||
| Goal | Edit |
|
||||
|------|------|
|
||||
| ------------------------------------------------ | ---------------------------------------------- |
|
||||
| Change the default agent or model | Local `agent-orchestrator.yaml` |
|
||||
| Add `.env` or tool config files to each worktree | Local `symlinks` |
|
||||
| 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
|
||||
|
||||
| Key | Default | Purpose |
|
||||
|-----|---------|---------|
|
||||
| --------------------- | ------------------------- | ------------------------------------------------------------ |
|
||||
| `port` | `3000` | Dashboard HTTP port |
|
||||
| `terminalPort` | auto from `14800` | tmux terminal WebSocket port |
|
||||
| `directTerminalPort` | auto from `14801` | direct PTY WebSocket port |
|
||||
|
|
@ -172,7 +174,19 @@ ls ~/.agent-orchestrator
|
|||
## Next Steps
|
||||
|
||||
<Cards>
|
||||
<Card title="Projects" 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." />
|
||||
<Card
|
||||
title="Projects"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ agent: claude-code
|
|||
Built-in agents:
|
||||
|
||||
| Agent | Use when |
|
||||
|-------|----------|
|
||||
| ------------- | ------------------------------------------------------ |
|
||||
| `claude-code` | You want the default, most tested path |
|
||||
| `codex` | You want OpenAI Codex CLI sessions |
|
||||
| `aider` | You already use Aider workflows |
|
||||
|
|
@ -52,7 +52,7 @@ agentConfig:
|
|||
`permissions` accepts:
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| ---------------- | ----------------------------------------------------- |
|
||||
| `permissionless` | Let the agent edit and run commands without prompting |
|
||||
| `default` | Use the agent tool's normal permission behavior |
|
||||
| `auto-edit` | Auto-approve edits, ask for other actions |
|
||||
|
|
@ -69,7 +69,7 @@ runtime: tmux
|
|||
```
|
||||
|
||||
| Runtime | Use when |
|
||||
|---------|----------|
|
||||
| --------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `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 |
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ workspace: worktree
|
|||
```
|
||||
|
||||
| Workspace | Use when |
|
||||
|-----------|----------|
|
||||
| ---------- | ----------------------------------------------------------------- |
|
||||
| `worktree` | Default. Fast, disk-efficient, creates a git worktree per session |
|
||||
| `clone` | Slower, but gives each session a separate clone |
|
||||
|
||||
|
|
@ -173,7 +173,7 @@ scm:
|
|||
Built-in trackers:
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| -------- | ------------- |
|
||||
| `github` | GitHub issues |
|
||||
| `gitlab` | GitLab issues |
|
||||
| `linear` | Linear issues |
|
||||
|
|
@ -181,7 +181,7 @@ Built-in trackers:
|
|||
Built-in SCM plugins:
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| -------- | ---------------------------------------- |
|
||||
| `github` | GitHub PRs, checks, reviews, merge state |
|
||||
| `gitlab` | GitLab merge requests |
|
||||
|
||||
|
|
@ -234,7 +234,7 @@ opencodeIssueSessionStrategy: reuse
|
|||
`orchestratorSessionStrategy` accepts:
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| --------------- | ----------------------------------------------------- |
|
||||
| `reuse` | Attach to the existing orchestrator session |
|
||||
| `delete` | Delete the old session and start a new one |
|
||||
| `ignore` | Leave the old session and start another |
|
||||
|
|
@ -249,7 +249,7 @@ opencodeIssueSessionStrategy: reuse
|
|||
These fields are valid in a local project config:
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| ------------------------------ | ---------- | -------------------------------------------- |
|
||||
| `repo` | `string` | Optional legacy/local repo slug |
|
||||
| `defaultBranch` | `string` | Branch PRs target, usually `main` |
|
||||
| `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
|
||||
|
||||
<Cards>
|
||||
<Card title="Reactions" 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." />
|
||||
<Card
|
||||
title="Reactions"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -58,13 +58,14 @@ reactions:
|
|||
```
|
||||
|
||||
<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>
|
||||
|
||||
## Default Reactions
|
||||
|
||||
| Key | When it fires | Default action | Notes |
|
||||
|-----|---------------|----------------|-------|
|
||||
| -------------------- | ------------------------------------------- | --------------- | --------------------------------------------------- |
|
||||
| `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 |
|
||||
| `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 | Meaning |
|
||||
|--------|---------|
|
||||
| --------------- | ----------------------------------------------------------- |
|
||||
| `send-to-agent` | Sends `message` into the running agent session |
|
||||
| `notify` | Routes an event to configured notifiers |
|
||||
| `auto-merge` | Reserved merge intent; currently routes like a notification |
|
||||
|
|
@ -91,7 +92,7 @@ reactions:
|
|||
Each reaction accepts:
|
||||
|
||||
| Field | Type | Purpose |
|
||||
|-------|------|---------|
|
||||
| ---------------- | ------------------------------------------- | ---------------------------------------------------------- |
|
||||
| `auto` | `boolean` | Whether AO should perform the automated action |
|
||||
| `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 |
|
||||
|
|
@ -173,7 +174,7 @@ ao review-check <session-id>
|
|||
## Event Reference
|
||||
|
||||
| Event | Reaction |
|
||||
|-------|----------|
|
||||
| -------------------------- | -------------------- |
|
||||
| `ci.failing` | `ci-failed` |
|
||||
| `review.changes_requested` | `changes-requested` |
|
||||
| `automated_review.found` | `bugbot-comments` |
|
||||
|
|
@ -188,6 +189,14 @@ ao review-check <session-id>
|
|||
|
||||
<Cards>
|
||||
<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 title="Configuration" href="/docs/configuration" description="Understand global registry vs local project config." />
|
||||
<Card
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| ------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
||||
| `HOST=0.0.0.0` | Bind the Next.js dashboard to all interfaces |
|
||||
| `TERMINAL_PORT` | Override the tmux 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 | Default | Config key | Env var override | Purpose |
|
||||
|------|---------|------------|-----------------|---------|
|
||||
| ------- | ------------------ | -------------------- | ---------------------- | ------------------------------------ |
|
||||
| `3000` | dashboard HTTP | `port` | `PORT` | Next.js app + API routes |
|
||||
| `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 |
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ http://localhost:3000
|
|||
The main dashboard groups sessions by what you need to do next.
|
||||
|
||||
| Column | What it means | What you usually do |
|
||||
|--------|---------------|---------------------|
|
||||
| ----------- | -------------------------------------------------- | ----------------------------------------- |
|
||||
| **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 |
|
||||
| **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:
|
||||
|
||||
| Tab | Shows |
|
||||
|-----|-------|
|
||||
| ---------- | ---------------------------- |
|
||||
| **All** | Open, merged, and closed PRs |
|
||||
| **Open** | PRs still in progress |
|
||||
| **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:
|
||||
|
||||
| Backend | Used when | Default port |
|
||||
|---------|-----------|--------------|
|
||||
| -------------------- | ------------------ | ------------ |
|
||||
| tmux WebSocket mux | `runtime: tmux` | `14800` |
|
||||
| direct PTY WebSocket | `runtime: process` | `14801` |
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ The favicon and document title also change when sessions need attention, so you
|
|||
## Routes
|
||||
|
||||
| Route | Use for |
|
||||
|-------|---------|
|
||||
| ------------------------------------- | ----------------------------------------- |
|
||||
| `/` | Project overview or current project board |
|
||||
| `/projects/{projectId}` | One project's board |
|
||||
| `/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:
|
||||
|
||||
| Service | Default | Config |
|
||||
|---------|---------|--------|
|
||||
| ----------------------- | ----------------- | ---------------------------------------------- |
|
||||
| Dashboard HTTP | `3000` | `port` or `PORT` |
|
||||
| tmux terminal WebSocket | auto from `14800` | `terminalPort` or `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>
|
||||
<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 title="Remote access" href="/docs/configuration/remote-access" description="Open the dashboard from another device safely." />
|
||||
<Card
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -258,7 +258,19 @@ projects:
|
|||
## Next Steps
|
||||
|
||||
<Cards>
|
||||
<Card title="Configuration Reference" 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." />
|
||||
<Card
|
||||
title="Configuration Reference"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
</Accordion>
|
||||
|
||||
<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).
|
||||
</Accordion>
|
||||
<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).
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
<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.
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
<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.
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
<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.
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
<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.
|
||||
</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.
|
||||
</Accordion>
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
<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.
|
||||
</Accordion>
|
||||
|
||||
<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).
|
||||
</Accordion>
|
||||
<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).
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
<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.
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
<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.
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
</Accordion>
|
||||
<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.
|
||||
</Accordion>
|
||||
|
||||
<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.
|
||||
|
|
|
|||
|
|
@ -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`).
|
||||
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).
|
||||
|
||||
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:
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
<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 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" />
|
||||
<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
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
{
|
||||
"title": "Guides",
|
||||
"defaultOpen": false,
|
||||
"pages": [
|
||||
"per-role-agents",
|
||||
"parallel-issues",
|
||||
"ci-recovery",
|
||||
"review-loop",
|
||||
"reactions",
|
||||
"multi-project"
|
||||
]
|
||||
"pages": ["per-role-agents", "parallel-issues", "ci-recovery", "review-loop", "reactions", "multi-project"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,5 +93,6 @@ ao stop --all # stop everything
|
|||
```
|
||||
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ AO creates two distinct sessions. Compare the PRs side by side.
|
|||
## Isolation guarantees
|
||||
|
||||
| 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/`. |
|
||||
| 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 |
|
||||
|
|
@ -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 --dry-run` — preview first
|
||||
|
||||
<Callout type="info">
|
||||
Need them all gone? `ao stop --all` halts every running AO instance on this machine.
|
||||
</Callout>
|
||||
<Callout type="info">Need them all gone? `ao stop --all` halts every running AO instance on this machine.</Callout>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ defaults:
|
|||
```
|
||||
|
||||
<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>
|
||||
|
||||
## Per-project override
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ reactions:
|
|||
```
|
||||
|
||||
<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>
|
||||
|
||||
## Recipe: Custom CI failure message
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ A single structured prompt with:
|
|||
- A pointer to the PR head SHA
|
||||
|
||||
<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>
|
||||
|
||||
## 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:
|
||||
|
||||
| Login | Tool |
|
||||
|---|---|
|
||||
| ------------------------- | -------------- |
|
||||
| `cursor[bot]` | Cursor AI |
|
||||
| `github-actions[bot]` | GitHub Actions |
|
||||
| `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]`):
|
||||
|
||||
| Login | Tool |
|
||||
|---|---|
|
||||
| ------------------ | --------------------- |
|
||||
| `gitlab-bot` | GitLab built-in |
|
||||
| `ghost` | Deleted / system user |
|
||||
| `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.
|
||||
|
||||
<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>
|
||||
|
||||
## 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 handles | You still handle |
|
||||
| --- | --- |
|
||||
| ---------------------------------- | -------------------------------------------- |
|
||||
| Creating isolated workspaces | Choosing good issues |
|
||||
| Launching and monitoring agents | Reviewing code and behavior |
|
||||
| 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.
|
||||
|
||||
| Plugin slot | What it controls | Examples |
|
||||
| --- | --- | --- |
|
||||
| ----------- | --------------------------------- | ------------------------------------------- |
|
||||
| Agent | Which coding tool writes changes | Claude Code, Codex, Cursor, Aider, OpenCode |
|
||||
| Runtime | How the agent process runs | tmux, child process |
|
||||
| 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"
|
||||
linux="full"
|
||||
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
|
||||
|
||||
<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="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 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" />
|
||||
<Card
|
||||
title="Guides"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
<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>
|
||||
|
||||
## Prerequisites
|
||||
|
|
@ -20,13 +21,18 @@ This page gets your machine ready to run Agent Orchestrator. By the end, the `ao
|
|||
macos="full"
|
||||
linux="full"
|
||||
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:
|
||||
|
||||
| Tool | Required | Why AO needs it |
|
||||
| --- | --- | --- |
|
||||
| --------------- | ---------------------- | ------------------------------------------------------------------------------------ |
|
||||
| Node.js 20+ | Yes | Runs the AO CLI, dashboard, and plugins. |
|
||||
| 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. |
|
||||
|
|
@ -38,29 +44,12 @@ If you plan to use GitLab or Linear, install and authenticate their CLIs or cred
|
|||
## Install AO
|
||||
|
||||
<Tabs items={["npm", "pnpm", "yarn", "from source"]}>
|
||||
<Tab value="npm">
|
||||
```bash
|
||||
npm install -g @aoagents/ao
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pnpm">
|
||||
```bash
|
||||
pnpm add -g @aoagents/ao
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="yarn">
|
||||
```bash
|
||||
yarn global add @aoagents/ao
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="npm">```bash npm install -g @aoagents/ao ```</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">
|
||||
```bash
|
||||
git clone https://github.com/ComposioHQ/agent-orchestrator
|
||||
cd agent-orchestrator
|
||||
pnpm install
|
||||
pnpm build
|
||||
pnpm --filter @aoagents/ao link --global
|
||||
```
|
||||
```bash git clone https://github.com/ComposioHQ/agent-orchestrator cd agent-orchestrator pnpm install pnpm build
|
||||
pnpm --filter @aoagents/ao link --global ```
|
||||
</Tab>
|
||||
</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.
|
||||
|
||||
<Tabs items={["Claude Code", "Codex", "Cursor", "Aider", "OpenCode"]}>
|
||||
<Tab value="Claude Code">
|
||||
```bash
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
claude
|
||||
```
|
||||
</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>
|
||||
<Tab value="Claude Code">```bash npm install -g @anthropic-ai/claude-code claude ```</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>
|
||||
|
||||
</Step>
|
||||
|
|
@ -181,10 +144,12 @@ Use Slack, Discord, webhook, or another network notifier for alerts. Desktop not
|
|||
|
||||
<Accordions>
|
||||
<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 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 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.
|
||||
|
|
@ -204,6 +169,10 @@ Use Slack, Discord, webhook, or another network notifier for alerts. Desktop not
|
|||
|
||||
<Cards>
|
||||
<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" />
|
||||
</Cards>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ The platform differences come from the tools used to run and attach to long-live
|
|||
## Recommended Setup
|
||||
|
||||
| Platform | Runtime | Notifications | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| ---------------------- | ------------------- | ---------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| 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`. |
|
||||
| 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:
|
||||
|
||||
| Capability | Status |
|
||||
| --- | --- |
|
||||
| ----------------------------------------- | --------- |
|
||||
| tmux-backed worker sessions | Supported |
|
||||
| Browser dashboard terminal | Supported |
|
||||
| iTerm2 attach/open helpers | Supported |
|
||||
|
|
@ -55,7 +55,8 @@ What works well on macOS:
|
|||
| macOS idle sleep prevention while AO runs | Supported |
|
||||
|
||||
<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>
|
||||
|
||||
## Linux
|
||||
|
|
@ -80,7 +81,7 @@ defaults:
|
|||
What to know:
|
||||
|
||||
| Capability | Status |
|
||||
| --- | --- |
|
||||
| --------------------------- | ----------------------------------------------------------- |
|
||||
| tmux-backed worker sessions | Supported |
|
||||
| Browser dashboard terminal | Supported |
|
||||
| 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
|
||||
|
||||
<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>
|
||||
|
||||
Recommended config:
|
||||
|
|
@ -107,7 +109,7 @@ defaults:
|
|||
What works:
|
||||
|
||||
| Capability | Status |
|
||||
| --- | --- |
|
||||
| --------------------------------------------------------- | ------------------------------------------------------- |
|
||||
| `ao start`, `ao stop`, `ao dashboard` | Supported |
|
||||
| `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 |
|
||||
|
|
@ -117,7 +119,7 @@ What works:
|
|||
What is limited:
|
||||
|
||||
| Capability | Status | Use instead |
|
||||
| --- | --- | --- |
|
||||
| --------------------------- | ---------------------- | ---------------------------------------------- |
|
||||
| `runtime: tmux` | Not available natively | `runtime: process` |
|
||||
| iTerm2 terminal integration | Not available | Browser dashboard terminal |
|
||||
| Desktop notifier | No-op on Windows | Slack, Discord, webhook, Composio, or OpenClaw |
|
||||
|
|
@ -148,7 +150,7 @@ Operational notes:
|
|||
## Choosing A Runtime
|
||||
|
||||
| Runtime | Best for | Tradeoff |
|
||||
| --- | --- | --- |
|
||||
| --------- | ------------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| `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 |
|
||||
|
||||
|
|
|
|||
|
|
@ -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" }}>
|
||||
<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>
|
||||
|
||||
[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
|
||||
|
||||
| Variable | Set by AO | Purpose |
|
||||
|---|---|---|
|
||||
| --------------- | --------- | -------------------------- |
|
||||
| `AO_SESSION_ID` | ✓ | AO session id |
|
||||
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
||||
| `PATH` | ✓ | Prepends `~/.ao/bin` |
|
||||
|
|
@ -48,7 +50,8 @@ agent: aider
|
|||
|
||||
<Accordions>
|
||||
<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 title="Dashboard cost shows $0">
|
||||
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" }}>
|
||||
<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>
|
||||
|
||||
[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
|
||||
|
||||
| Variable | Set by AO | Purpose |
|
||||
|---|---|---|
|
||||
| --------------- | ------------------------------ | --------------------------------------------------- |
|
||||
| `AO_SESSION_ID` | ✓ | Unique AO session identifier |
|
||||
| `AO_ISSUE_ID` | ✓ (when spawned from an issue) | Issue identifier |
|
||||
| `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" }}>
|
||||
<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>
|
||||
|
||||
[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
|
||||
|
||||
| Variable | Set by AO | Purpose |
|
||||
|---|---|---|
|
||||
| ---------------------------- | --------- | --------------------------------------------------- |
|
||||
| `AO_SESSION_ID` | ✓ | AO session id |
|
||||
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
||||
| `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" }}>
|
||||
<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>
|
||||
|
||||
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
|
||||
|
||||
| Variable | Set by AO | Purpose |
|
||||
|---|---|---|
|
||||
| --------------- | --------- | -------------------------- |
|
||||
| `AO_SESSION_ID` | ✓ | AO session id |
|
||||
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
||||
| `PATH` | ✓ | Prepends `~/.ao/bin` |
|
||||
|
|
@ -60,9 +62,11 @@ The plugin's `detect()` checks `agent --help` output for the strings `Cursor Age
|
|||
|
||||
<Accordions>
|
||||
<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 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>
|
||||
</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.
|
||||
|
||||
<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>
|
||||
|
||||
## Supported agents
|
||||
|
||||
| Agent | Slot name | Binary | Session resume |
|
||||
|---|---|---|---|
|
||||
| ----------------------------------------------- | ------------- | ---------- | ---------------------------- |
|
||||
| [Claude Code](/docs/plugins/agents/claude-code) | `claude-code` | `claude` | ✅ `--resume` |
|
||||
| [Codex](/docs/plugins/agents/codex) | `codex` | `codex` | ✅ `codex resume <threadId>` |
|
||||
| [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 |
|
||||
|
||||
<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="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="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>
|
||||
|
||||
## Choosing
|
||||
|
|
|
|||
|
|
@ -1,10 +1,4 @@
|
|||
{
|
||||
"title": "Agents",
|
||||
"pages": [
|
||||
"claude-code",
|
||||
"codex",
|
||||
"cursor",
|
||||
"aider",
|
||||
"opencode"
|
||||
]
|
||||
"pages": ["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" }}>
|
||||
<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>
|
||||
|
||||
[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
|
||||
|
||||
| Variable | Set by AO | Purpose |
|
||||
|---|---|---|
|
||||
| --------------- | --------- | -------------------------- |
|
||||
| `AO_SESSION_ID` | ✓ | AO session id |
|
||||
| `AO_ISSUE_ID` | ✓ | Issue identifier |
|
||||
| `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:
|
||||
|
||||
| Prompt | Example input |
|
||||
|--------|--------------|
|
||||
| ------------------- | ------------------------------------------------------------ |
|
||||
| Plugin display name | `PagerDuty` |
|
||||
| Plugin slot | `notifier` (selected from a list of all 7 slots) |
|
||||
| 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
|
||||
|
||||
| Method | Signature | Required |
|
||||
|--------|-----------|---------|
|
||||
| --------------- | ------------------------------------------------------------ | -------- |
|
||||
| `create` | `(config: RuntimeCreateConfig) => Promise<RuntimeHandle>` | yes |
|
||||
| `destroy` | `(handle: RuntimeHandle) => 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:**
|
||||
|
||||
| Method | Purpose | Returns `null`? |
|
||||
|--------|---------|----------------|
|
||||
| ------------------ | ----------------------------------------------------------------- | -------------------- |
|
||||
| `getLaunchCommand` | Shell command to start the agent | No |
|
||||
| `getEnvironment` | Env vars for the process (must include `~/.ao/bin` in PATH) | 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:**
|
||||
|
||||
| Method | Purpose | When to skip |
|
||||
|--------|---------|-------------|
|
||||
| --------------------- | ---------------------------------------- | ------------------------------------- |
|
||||
| `getRestoreCommand` | Resume a previous session | Agent has no resume capability |
|
||||
| `setupWorkspaceHooks` | Install metadata hooks for PR tracking | Never — required for the dashboard |
|
||||
| `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) |
|
||||
|
||||
<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>
|
||||
|
||||
### Workspace
|
||||
|
||||
| Method | Signature | Required |
|
||||
|--------|-----------|---------|
|
||||
| ------------ | ---------------------------------------------------------------------------------- | -------- |
|
||||
| `create` | `(config: WorkspaceCreateConfig) => Promise<WorkspaceInfo>` | yes |
|
||||
| `destroy` | `(workspacePath: string) => Promise<void>` | 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
|
||||
|
||||
| Method | Signature | Required |
|
||||
|--------|-----------|---------|
|
||||
| ---------------- | ------------------------------------------------------------------------------------ | -------- |
|
||||
| `getIssue` | `(identifier: string, project: ProjectConfig) => Promise<Issue>` | yes |
|
||||
| `isCompleted` | `(identifier: string, project: ProjectConfig) => Promise<boolean>` | yes |
|
||||
| `issueUrl` | `(identifier: string, project: ProjectConfig) => string` | yes |
|
||||
|
|
@ -300,7 +302,7 @@ The richest interface — covers PR lifecycle, CI tracking, review tracking, and
|
|||
### Notifier
|
||||
|
||||
| Method | Signature | Required |
|
||||
|--------|-----------|---------|
|
||||
| ------------------- | ----------------------------------------------------------------------- | -------- |
|
||||
| `notify` | `(event: OrchestratorEvent) => Promise<void>` | yes |
|
||||
| `notifyWithActions` | `(event: OrchestratorEvent, actions: NotifyAction[]) => Promise<void>` | 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
|
||||
|
||||
| Method | Signature | Required |
|
||||
|--------|-----------|---------|
|
||||
| --------------- | ---------------------------------------- | -------- |
|
||||
| `openSession` | `(session: Session) => Promise<void>` | yes |
|
||||
| `openAll` | `(sessions: Session[]) => Promise<void>` | yes |
|
||||
| `isSessionOpen` | `(session: Session) => Promise<boolean>` | optional |
|
||||
|
|
@ -421,7 +423,7 @@ projects:
|
|||
## `ao plugin` subcommands
|
||||
|
||||
| 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 search <query>` | Search the bundled catalog by name, package, description, or slot. |
|
||||
| `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:**
|
||||
|
||||
| Export | Purpose |
|
||||
|--------|---------|
|
||||
| ------------- | --------------------------------------------------------------------------------------- |
|
||||
| `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. |
|
||||
|
||||
**Activity detection (agent plugins):**
|
||||
|
||||
| Export | Purpose |
|
||||
|--------|---------|
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| `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`). |
|
||||
| `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):**
|
||||
|
||||
| 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). |
|
||||
| `buildAgentPath` | Prepend `~/.ao/bin` to a PATH string. |
|
||||
| `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:**
|
||||
|
||||
| Export | Value |
|
||||
|--------|-------|
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| `DEFAULT_READY_THRESHOLD_MS` | `300_000` (5 min) — ready → idle threshold |
|
||||
| `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 |
|
||||
|
|
@ -525,7 +527,7 @@ For agent plugins, the required `getActivityState` tests are listed in the CLAUD
|
|||
## Common pitfalls
|
||||
|
||||
| Pitfall | Correct approach |
|
||||
|---------|-----------------|
|
||||
| ------------------------------------ | ------------------------------------------------------------------ |
|
||||
| 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 |
|
||||
| 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.
|
||||
|
||||
<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>
|
||||
|
||||
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
|
||||
|
||||
<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="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>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ AO has **eight plugin slots**. Only one plugin per slot is active at a time, and
|
|||
## Slots at a glance
|
||||
|
||||
| Slot | Default | What it does |
|
||||
|---|---|---|
|
||||
| ------------- | ----------------------------------------- | -------------------------------------------- |
|
||||
| **Agent** | `claude-code` | Which AI tool writes the code |
|
||||
| **Runtime** | `tmux` (macOS/Linux), `process` (Windows) | Where the agent process runs |
|
||||
| **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
|
||||
|
||||
<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 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." />
|
||||
<PluginCard
|
||||
name="Claude Code"
|
||||
logo="claude-code"
|
||||
href="/docs/plugins/agents/claude-code"
|
||||
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>
|
||||
|
||||
## Runtimes
|
||||
|
||||
<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 name="process" logo="windows" href="/docs/plugins/runtimes/process" description="Cross-platform child-process runtime. Required on Windows." />
|
||||
<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
|
||||
name="process"
|
||||
logo="windows"
|
||||
href="/docs/plugins/runtimes/process"
|
||||
description="Cross-platform child-process runtime. Required on Windows."
|
||||
/>
|
||||
</PluginGrid>
|
||||
|
||||
## Workspaces
|
||||
|
||||
<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 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." />
|
||||
<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
|
||||
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>
|
||||
|
||||
## Trackers
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard name="GitHub" 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." />
|
||||
<PluginCard
|
||||
name="GitHub"
|
||||
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>
|
||||
|
||||
## SCM
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard 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." />
|
||||
<PluginCard
|
||||
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>
|
||||
|
||||
## Notifiers
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard name="Dashboard" 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="Dashboard"
|
||||
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="Webhook" 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." />
|
||||
<PluginCard
|
||||
name="Webhook"
|
||||
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>
|
||||
|
||||
## Terminals
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard 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." />
|
||||
<PluginCard
|
||||
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>
|
||||
|
||||
## Writing your own
|
||||
|
|
|
|||
|
|
@ -1,14 +1,5 @@
|
|||
{
|
||||
"title": "Plugins",
|
||||
"defaultOpen": false,
|
||||
"pages": [
|
||||
"agents",
|
||||
"runtimes",
|
||||
"workspaces",
|
||||
"trackers",
|
||||
"scm",
|
||||
"notifiers",
|
||||
"terminals",
|
||||
"authoring"
|
||||
]
|
||||
"pages": ["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" }}>
|
||||
<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>
|
||||
|
||||
<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
|
||||
|
||||
|
|
@ -37,7 +44,7 @@ notificationRouting:
|
|||
```
|
||||
|
||||
| Config key | Default | What it does |
|
||||
|---|---|---|
|
||||
| -------------- | ---------------------- | ------------------------------------------------------------ |
|
||||
| `backend` | `auto` | `ao-app`, `terminal-notifier`, `osascript`, or auto fallback |
|
||||
| `dashboardUrl` | dashboard port | URL opened from desktop notification actions |
|
||||
| `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.
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard name="Dashboard" 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="Dashboard"
|
||||
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="Webhook" 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." />
|
||||
<PluginCard
|
||||
name="Webhook"
|
||||
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>
|
||||
|
||||
## Stacking notifiers
|
||||
|
|
@ -57,7 +87,7 @@ a real test message.
|
|||
## What gets notified
|
||||
|
||||
| Event | When |
|
||||
|---|---|
|
||||
| ------------------- | -------------------------------------- |
|
||||
| `session.spawned` | `ao spawn` succeeds |
|
||||
| `session.working` | Agent is actively editing code |
|
||||
| `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" }}>
|
||||
<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>
|
||||
|
||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||
|
|
@ -70,7 +72,7 @@ The OpenClaw config keeps the actual secret:
|
|||
## Config
|
||||
|
||||
| Key | Default | What it does |
|
||||
|---|---|---|
|
||||
| -------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------- |
|
||||
| `url` | `http://127.0.0.1:18789/hooks/agent` | OpenClaw hook endpoint |
|
||||
| `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 |
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
| Plugin | Best for | Binary needed |
|
||||
|---|---|---|
|
||||
| ----------------------------------------- | ---------------------------------------------------- | ------------- |
|
||||
| [tmux](/docs/plugins/runtimes/tmux) | macOS / Linux default. You can attach interactively. | `tmux` |
|
||||
| [process](/docs/plugins/runtimes/process) | Windows, Docker, CI-like environments | — |
|
||||
|
||||
<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 name="process" logo="windows" href="/docs/plugins/runtimes/process" description="Cross-platform child process. Required on Windows." />
|
||||
<PluginCard
|
||||
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>
|
||||
|
||||
## 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'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" }}>
|
||||
<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>
|
||||
|
||||
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" }}>
|
||||
<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>
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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" }}>
|
||||
<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>
|
||||
|
||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||
|
|
@ -59,7 +61,7 @@ projects:
|
|||
Full `webhook.*` sub-object:
|
||||
|
||||
| Field | Default | Description |
|
||||
|---|---|---|
|
||||
| ----------------- | --------------------- | ------------------------------------------- |
|
||||
| `enabled` | `true` | Enable or disable webhook processing |
|
||||
| `path` | `/api/webhooks` | Override the receive path |
|
||||
| `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" }}>
|
||||
<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>
|
||||
|
||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||
|
|
@ -25,7 +27,7 @@ scmConfig:
|
|||
## Mapping
|
||||
|
||||
| Concept | GitHub term | GitLab term |
|
||||
|---|---|---|
|
||||
| -------------- | ------------ | ----------------- |
|
||||
| Review request | Pull request | Merge request |
|
||||
| Review | Review | Discussion / note |
|
||||
| CI status | Check runs | Pipelines / jobs |
|
||||
|
|
@ -65,7 +67,7 @@ projects:
|
|||
Full `webhook.*` sub-object:
|
||||
|
||||
| Field | Default | Description |
|
||||
|---|---|---|
|
||||
| ----------------- | --------------------- | ---------------------------------------- |
|
||||
| `enabled` | `true` | Enable or disable webhook processing |
|
||||
| `path` | `/api/webhooks` | Override the receive path |
|
||||
| `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:**
|
||||
|
||||
| Username |
|
||||
|---|
|
||||
| ------------------ |
|
||||
| `gitlab-bot` |
|
||||
| `ghost` |
|
||||
| `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.
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard 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." />
|
||||
<PluginCard
|
||||
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>
|
||||
|
||||
## 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.
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard 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." />
|
||||
<PluginCard
|
||||
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>
|
||||
|
||||
## 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" }}>
|
||||
<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>
|
||||
|
||||
<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" }}>
|
||||
<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>
|
||||
|
||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||
|
|
@ -21,7 +23,7 @@ terminalConfig:
|
|||
```
|
||||
|
||||
| Config key | Default | What it does |
|
||||
|---|---|---|
|
||||
| -------------- | ----------------------- | ---------------------------------------------------------------------------------- |
|
||||
| `dashboardUrl` | `http://localhost:3000` | Base URL for the dashboard. Override when the dashboard runs on another host/port. |
|
||||
|
||||
## 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" }}>
|
||||
<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>
|
||||
|
||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||
|
|
@ -32,7 +34,7 @@ projects:
|
|||
## How it's used
|
||||
|
||||
| Operation | `gh` command invoked |
|
||||
|---|---|
|
||||
| ---------------- | ------------------------------------------- |
|
||||
| Fetch issue | `gh issue view <num> --json title,body,...` |
|
||||
| Create issue | `gh issue create` |
|
||||
| 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" }}>
|
||||
<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>
|
||||
|
||||
<PlatformSupport macos="full" linux="full" windows="full" />
|
||||
|
|
@ -28,11 +30,12 @@ projects:
|
|||
```
|
||||
|
||||
| Config key | Default | What it does |
|
||||
|---|---|---|
|
||||
| ---------- | ------------ | ---------------------------------------------------- |
|
||||
| `host` | `gitlab.com` | GitLab hostname — override for self-hosted instances |
|
||||
|
||||
<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>
|
||||
|
||||
## 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.
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard name="GitHub" 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." />
|
||||
<PluginCard
|
||||
name="GitHub"
|
||||
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>
|
||||
|
||||
## 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" }}>
|
||||
<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>
|
||||
|
||||
<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
|
||||
|
||||
| Key | Required | What it does |
|
||||
|---|---|---|
|
||||
| --------------- | --------------------- | ---------------------------------------------------------- |
|
||||
| `teamId` | ✓ (for `createIssue`) | Linear team the issue lives in |
|
||||
| `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" }}>
|
||||
<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>
|
||||
|
||||
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 |
|
||||
|---|---|---|
|
||||
| ---------- | -------------- | ----------------------------- |
|
||||
| `cloneDir` | `~/.ao-clones` | Base directory for all clones |
|
||||
|
||||
## 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:
|
||||
|
||||
| Plugin | Isolation | Disk usage | Speed |
|
||||
|---|---|---|---|
|
||||
| --------------------------------------------- | --------------------------------- | ---------------------- | ---------------------- |
|
||||
| [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) |
|
||||
|
||||
<PluginGrid>
|
||||
<PluginCard 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." />
|
||||
<PluginCard
|
||||
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>
|
||||
|
||||
## 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" }}>
|
||||
<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>
|
||||
|
||||
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 |
|
||||
|---|---|---|
|
||||
| ------------- | -------------- | -------------------------------- |
|
||||
| `worktreeDir` | `~/.worktrees` | Base directory for all worktrees |
|
||||
|
||||
## How it works
|
||||
|
|
@ -45,7 +47,7 @@ projects:
|
|||
```
|
||||
|
||||
| Knob | Purpose |
|
||||
|---|---|
|
||||
| ------------ | ------------------------------------------------------------------------------------------------- |
|
||||
| `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`) |
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
<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>
|
||||
|
||||
## Pick A Safe First Task
|
||||
|
|
@ -55,14 +56,16 @@ Open a second terminal in the same repository.
|
|||
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 value="Freeform prompt">
|
||||
```bash
|
||||
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>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -127,7 +130,7 @@ ao session cleanup
|
|||
## What AO Created
|
||||
|
||||
| Item | What it means |
|
||||
| --- | --- |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| `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. |
|
||||
| Worker session | The agent process that works on one issue or prompt. |
|
||||
|
|
@ -137,7 +140,7 @@ ao session cleanup
|
|||
## If Something Looks Wrong
|
||||
|
||||
| Symptom | First check |
|
||||
| --- | --- |
|
||||
| --------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| 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
|
||||
|
||||
<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="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" />
|
||||
<Card
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -59,13 +59,17 @@ Covers: install health, plugin resolution, notifier connectivity, stale temp fil
|
|||
|
||||
<Accordions>
|
||||
<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 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 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 title="Webhook delivers but nothing happens">
|
||||
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.
|
||||
</Accordion>
|
||||
<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>
|
||||
</Accordions>
|
||||
|
||||
|
|
@ -101,7 +106,7 @@ Covers: install health, plugin resolution, notifier connectivity, stale temp fil
|
|||
|
||||
- Check the [FAQ](/docs/faq).
|
||||
- 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`
|
||||
- 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> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://api.github.com/repos/ComposioHQ/agent-orchestrator",
|
||||
{
|
||||
const response = await fetch("https://api.github.com/repos/ComposioHQ/agent-orchestrator", {
|
||||
next: { revalidate: 3600 },
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
"User-Agent": "ao-website",
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return FALLBACK_STATS;
|
||||
|
|
|
|||
|
|
@ -103,7 +103,9 @@ a {
|
|||
.landing-card {
|
||||
background: var(--landing-card-bg);
|
||||
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 {
|
||||
|
|
@ -155,7 +157,9 @@ a {
|
|||
.landing-reveal {
|
||||
opacity: 0;
|
||||
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 {
|
||||
|
|
@ -413,110 +417,288 @@ a {
|
|||
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. */
|
||||
|
||||
.landing-page .mt-1 { margin-top: calc(var(--spacing) * 1); }
|
||||
.landing-page .mt-6 { margin-top: calc(var(--spacing) * 6); }
|
||||
.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 .mt-1 {
|
||||
margin-top: calc(var(--spacing) * 1);
|
||||
}
|
||||
.landing-page .mt-6 {
|
||||
margin-top: calc(var(--spacing) * 6);
|
||||
}
|
||||
.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-2 { margin-bottom: calc(var(--spacing) * 2); }
|
||||
.landing-page .mb-3 { margin-bottom: calc(var(--spacing) * 3); }
|
||||
.landing-page .mb-4 { 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 .mb-1 {
|
||||
margin-bottom: calc(var(--spacing) * 1);
|
||||
}
|
||||
.landing-page .mb-2 {
|
||||
margin-bottom: calc(var(--spacing) * 2);
|
||||
}
|
||||
.landing-page .mb-3 {
|
||||
margin-bottom: calc(var(--spacing) * 3);
|
||||
}
|
||||
.landing-page .mb-4 {
|
||||
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-2 { margin-left: calc(var(--spacing) * 2); }
|
||||
.landing-page .ml-1\.5 { margin-left: calc(var(--spacing) * 1.5); }
|
||||
.landing-page .ml-1 {
|
||||
margin-left: calc(var(--spacing) * 1);
|
||||
}
|
||||
.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\.5 { margin-right: calc(var(--spacing) * 1.5); }
|
||||
.landing-page .mr-1 {
|
||||
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-3 { padding: calc(var(--spacing) * 3); }
|
||||
.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 .p-2 {
|
||||
padding: calc(var(--spacing) * 2);
|
||||
}
|
||||
.landing-page .p-3 {
|
||||
padding: calc(var(--spacing) * 3);
|
||||
}
|
||||
.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-32 { padding-top: calc(var(--spacing) * 32); }
|
||||
.landing-page .pt-10 {
|
||||
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-3 { padding-left: calc(var(--spacing) * 3); 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 .px-2 {
|
||||
padding-left: calc(var(--spacing) * 2);
|
||||
padding-right: calc(var(--spacing) * 2);
|
||||
}
|
||||
.landing-page .px-3 {
|
||||
padding-left: calc(var(--spacing) * 3);
|
||||
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-2 { padding-top: calc(var(--spacing) * 2); padding-bottom: calc(var(--spacing) * 2); }
|
||||
.landing-page .py-3 { padding-top: calc(var(--spacing) * 3); 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); }
|
||||
.landing-page .py-1 {
|
||||
padding-top: calc(var(--spacing) * 1);
|
||||
padding-bottom: calc(var(--spacing) * 1);
|
||||
}
|
||||
.landing-page .py-2 {
|
||||
padding-top: calc(var(--spacing) * 2);
|
||||
padding-bottom: calc(var(--spacing) * 2);
|
||||
}
|
||||
.landing-page .py-3 {
|
||||
padding-top: calc(var(--spacing) * 3);
|
||||
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 */
|
||||
.landing-page .py-\[100px\] { padding-top: 100px; padding-bottom: 100px; }
|
||||
.landing-page .py-\[120px\] { padding-top: 120px; padding-bottom: 120px; }
|
||||
.landing-page .pt-\[60px\] { padding-top: 60px; }
|
||||
.landing-page .pb-\[120px\] { padding-bottom: 120px; }
|
||||
.landing-page .py-\[100px\] {
|
||||
padding-top: 100px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
.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
|
||||
navigation. In light mode --color-fd-border = #d6d3d1 which looks bright on the dark
|
||||
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-default\)\] { border-color: var(--landing-border-default); }
|
||||
.landing-page .border-\[var\(--landing-border-strong\)\] { border-color: var(--landing-border-strong); }
|
||||
.landing-page .border-\[var\(--landing-border-subtle\)\] {
|
||||
border-color: var(--landing-border-subtle);
|
||||
}
|
||||
.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 } */
|
||||
.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 } */
|
||||
.landing-page .text-\[clamp\(1\.75rem\,4vw\,2\.75rem\)\] { font-size: clamp(1.75rem, 4vw, 2.75rem); }
|
||||
.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); }
|
||||
.landing-page .text-\[clamp\(1\.75rem\,4vw\,2\.75rem\)\] {
|
||||
font-size: clamp(1.75rem, 4vw, 2.75rem);
|
||||
}
|
||||
.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,
|
||||
so its higher-priority layer keeps elements hidden / wrong grid at desktop widths. */
|
||||
@media (min-width: 768px) {
|
||||
.landing-page .md\:flex { display: flex; }
|
||||
.landing-page .md\:block { display: block; }
|
||||
.landing-page .md\:hidden { display: none; }
|
||||
.landing-page .md\:flex {
|
||||
display: flex;
|
||||
}
|
||||
.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-3 { 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\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
.landing-page .md\:grid-cols-3 {
|
||||
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-6 { gap: calc(var(--spacing) * 6); }
|
||||
.landing-page .md\:gap-12 { gap: calc(var(--spacing) * 12); }
|
||||
.landing-page .md\:gap-0 {
|
||||
gap: 0;
|
||||
}
|
||||
.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-baseline { align-items: baseline; }
|
||||
.landing-page .md\:items-start { align-items: flex-start; }
|
||||
.landing-page .md\:items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.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-2 { order: 2; }
|
||||
.landing-page .md\:order-1 {
|
||||
order: 1;
|
||||
}
|
||||
.landing-page .md\:order-2 {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,27 +9,31 @@ touch a developer's real AO installation.
|
|||
## Two tiers
|
||||
|
||||
| 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`) |
|
||||
| **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
|
||||
|
||||
**The Go suite (fastest, cross-platform):**
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
go test -tags e2e ./internal/cli/... # run it
|
||||
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`).
|
||||
`-v` logs each `ao` invocation and its full output, which is the audit trail you
|
||||
get for free from `go test`.
|
||||
|
||||
**Fresh-machine install, in a clean container:**
|
||||
|
||||
```bash
|
||||
docker build -f test/cli/Dockerfile -t ao-cli-smoke .
|
||||
docker run --rm --init ao-cli-smoke
|
||||
```
|
||||
|
||||
> `--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.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue