|
|
||
|---|---|---|
| .. | ||
| README.md | ||
README.md
AO CLI Foundation
This page is the running decision log for the Agent Orchestrator CLI. Keep new
CLI decisions here as the command surface grows.
Current State
This branch implements the daemon-control foundation. AO now has a Go/Cobra
ao binary that can start, inspect, diagnose, and stop the local backend daemon
end to end.
What works now:
ao startstarts the daemon in the background and waits for/readyz.ao statusandao status --jsonreport stopped, stale, unhealthy,
not-ready, or ready daemon state.ao stopgracefully stops the daemon using the PID inrunning.json.ao daemonis the hidden internal daemon entrypoint used byao start.ao doctorchecks config, data dir, SQLite migrations, daemon state, and
local tool availability forgit,tmux, andzellij.ao completiongenerates shell completions forbash,zsh,fish, and
powershell.ao versionandao --versionprint build metadata.go run .still works as a compatibility wrapper aroundinternal/daemon.Run.
Manual smoke test:
cd backend
go build -o /tmp/ao ./cmd/ao
tmp=$(mktemp -d)
export AO_RUN_FILE="$tmp/running.json"
export AO_DATA_DIR="$tmp/data"
export AO_PORT=3037
/tmp/ao status --json
/tmp/ao doctor
/tmp/ao start
/tmp/ao status --json
/tmp/ao stop
/tmp/ao status --json
What is intentionally not implemented yet:
ao project ...ao spawnao session ...ao sendao events ...
Next steps:
- Add
/api/v1/projectson the daemon over a small project service. - Implement
ao project list/add/show/remove. - Wire production Session Manager dependencies: project-backed repo resolver,
tmux/zellij runtime registry, first agent adapter, and AgentMessenger. - Add
/api/v1/sessions, then implementao spawn,ao session ..., and
ao send. - Add
/eventsSSE and durable event-list reads, then implement
ao events tail/list.
Decision
AO will use a single Go CLI binary built with
Cobra.
The CLI is a thin client for the Go daemon. It should not call SQLite, runtime
adapters, agent adapters, workspace adapters, or SCM integrations directly. It
should start, discover, inspect, and command the daemon through the loopback API
and the existing running.json handshake.
Initial rules:
- The binary name is
ao. ao daemonis the hidden/internal entrypoint for the long-running daemon.- User-facing commands call the daemon over loopback after reading
running.json. - Commands that mutate core AO state go through HTTP API routes, not direct
stores. - Commands support predictable text output first and
--jsonwhere automation
is likely. - Do not introduce Viper in the foundation. Start with explicit flags and a
small config/client layer, then add config loading once the shape is real.
References
These projects inform the direction, but AO should keep its own command surface
smaller at first.
| Project | CLI stack | What to take |
|---|---|---|
| Gastown | Go + Cobra, with Charmbracelet packages for richer terminal UI | Simple cmd/<binary>/main.go delegating to internal command construction. Useful confirmation that Cobra is the right default for this size of Go CLI. |
| GitHub CLI | Go + Cobra | Command factories, explicit IO streams, JSON output, and testable command construction. |
| Docker CLI | Go + Cobra | Daemon/client split, command groups, signal handling, and plugin-aware CLI layout. |
| kubectl | Go + Cobra | Large command tree patterns and IO abstractions. It is a useful ceiling, not a shape to copy now. |
| Tailscale CLI | Go + ffcli | Useful daemon-backed product model: a CLI talks to a local daemon. Do not copy the framework choice. |
The old AO TypeScript CLI is a product/workflow reference only. We should not
port its implementation because it mixes CLI, storage, runtime, and project
logic in-process. The rewrite needs the CLI to sit outside the core daemon.
Current Legacy CLI Inventory
Inventory source: installed ao binary at version 0.9.2, plus the old
packages/cli/src/program.ts and packages/cli/src/commands/*.ts files.
Count:
- 25 public top-level commands, excluding Commander-generated
help. - 26 visible top-level commands if generated
helpis counted. - 64 explicit public command nodes when nested subcommands are counted.
- 1 hidden internal command:
completion __complete. - No aliases are registered in the old Commander source.
Top-level commands:
| Command | Legacy purpose | Foundation decision |
|---|---|---|
start |
Start orchestrator agent and dashboard | Keep, but redefine as daemon start. |
stop |
Stop orchestrator agent and dashboard | Keep, daemon stop. |
status |
Show all sessions and project/session health | Keep, daemon and session status. |
spawn |
Spawn a single agent session | Keep after session API exists. |
batch-spawn |
Spawn many sessions | Defer. |
session |
Manage sessions | Keep a smaller subset after session API exists. |
send |
Send a message to a session | Keep after messaging API exists. |
acknowledge |
Agent self-reporting hook | Defer or replace with internal API. |
report |
Agent workflow transition hook | Defer or replace with internal API. |
review-check |
Trigger agents from review comments | Defer. |
review |
Manage AO-local reviewer runs | Defer. |
dashboard |
Start web dashboard | Defer to Electron/frontend lane. |
open |
Open terminal/dashboard | Defer. |
verify |
Verify issue after staging check | Defer. |
doctor |
Run install/env/runtime checks | Keep. |
update |
Upgrade AO | Defer to packaging/release lane. |
setup |
Configure integrations | Defer. |
plugin |
Plugin marketplace/install flow | Defer. |
notify |
Notification test commands | Defer. |
project |
Manage registered projects | Keep after project API exists. |
migrate-storage |
Legacy storage migration | Drop for rewrite unless a real migration appears. |
completion |
Generate shell completions | Keep. |
events |
Query activity event log | Keep a small tail/list surface after event API exists. |
config |
Read/write old global config | Defer. Avoid until config shape is stable. |
config-help |
Print old config schema | Drop. |
Nested legacy commands:
| Parent | Subcommands |
|---|---|
session |
ls, attach, kill, cleanup, claim-pr, restore, remap |
review |
run, execute, send, list |
setup |
dashboard, desktop, webhook, slack, discord, composio, composio-slack, composio-discord, composio-discord-bot, composio-mail, openclaw |
plugin |
list, search, create, install, update, uninstall |
project |
ls, add, rm, set-default |
events |
list, search, stats |
config |
set, get |
notify |
test |
completion |
zsh, hidden __complete |
Initial Command Surface
The first CLI should make AO installable, startable, inspectable, and stoppable
before trying to recreate the old product surface.
Foundation Commands
These are the first commands to implement.
| Command | Purpose | Notes |
|---|---|---|
ao start |
Start the daemon, wait for /readyz, and print PID/port. |
Reads the same config env as the daemon. Should be idempotent when an existing healthy daemon is already running. |
ao stop |
Stop the running daemon. | Reads running.json, sends graceful termination, waits for run-file removal, and reports stale/dead daemon state clearly. |
ao status |
Show daemon status and, once APIs exist, project/session summary. | First version can show run-file, process liveness, /healthz, /readyz, uptime, and port. Add --json; add --watch once useful. |
ao daemon |
Hidden internal daemon entrypoint. | This replaces the current direct go run . daemon entrypoint once main.go is extracted into internal/daemon. |
ao doctor |
Diagnose the local environment. | Start with daemon/run-file/port checks, required binaries, config dir/data dir permissions, and runtime availability. |
ao completion |
Generate shell completions. | Cobra can support bash, zsh, fish, and powershell. |
ao version |
Print CLI and build metadata. | Implement as both ao version and Cobra's --version flag. |
This gives a useful first release even before project/session mutation routes are
complete.
First Core Application Commands
These are the next commands once daemon HTTP routes expose the needed managers.
| Command | Purpose | Depends on |
|---|---|---|
ao project list |
List registered projects. | Project API. Alias ls is acceptable for old muscle memory. |
ao project add <path-or-url> |
Register a project. | Project API and project identity rules. |
ao project show <id> |
Inspect project config and health. | Project API. |
ao project remove <id> |
Archive/remove a project. | Project API. Alias rm is acceptable. |
ao spawn [issue] |
Spawn one coding-agent session. | Session Manager HTTP route, tracker lookup, workspace/runtime/agent adapters. |
ao session list |
List sessions across projects or one project. | Session API. Alias ls is acceptable. |
ao session show <session> |
Show one session with lifecycle, PR, CI, runtime, and paths. | Session API. |
ao session attach <session> |
Attach to the runtime terminal. | Runtime API or direct terminal attach contract exposed by daemon. |
ao session kill <session> |
Kill a session and clean up safely. | Session Manager Kill. |
ao session restore <session> |
Restore a terminated/crashed session. | Session Manager Restore. |
ao send <session> [message...] |
Send instructions to a running session. | AgentMessenger route. |
ao events tail |
Follow daemon activity events. | SSE/CDC API. |
ao events list |
List recent activity events. | Event read API. |
This is the smallest surface that covers the core product loop:
- Register a repo.
- Start AO.
- Spawn work.
- Inspect work.
- Intervene in work.
- Stop AO.
Explicit Deferrals
Do not include these in the CLI foundation:
batch-spawn: valuable, but it multiplies error handling before single-spawn
semantics are stable.dashboardandopen: frontend/Electron should own the primary dashboard
launch path first.review,review-check, andverify: useful workflow automation, but not
required to run core AO.setup,plugin, andnotify: integration/plugin surface should come after
the daemon API and config model settle.update: belongs with distribution and release packaging.configandconfig-help: wait for a stable Go config model. Avoid copying
the old TypeScript global config behavior.migrate-storage: old storage migration is not part of the rewrite unless a
concrete migration requirement appears.acknowledgeandreport: these are agent self-reporting hooks. Prefer a
daemon/internal protocol before exposing them as durable user CLI commands.
Implementation Plan
- Add Cobra to
backend/go.mod. - Move current daemon startup from
backend/main.gointo
backend/internal/daemon.Run(ctx, opts). - Add
backend/cmd/ao/main.goas the only user binary entrypoint. - Add
backend/internal/clifor command construction, IO streams, process
launching, run-file discovery, loopback HTTP client, and output formatting. - Implement
ao daemonfirst so the current daemon behavior is preserved. - Implement
ao start,ao stop, andao statusaroundrunning.jsonand
/healthz//readyz. - Add
ao doctor,ao completion, andao version. - Add command tests using Cobra command construction with fake IO, fake process
runner, and fake daemon client. Keep daemon integration tests in the daemon
packages.
Suggested package layout:
backend/
cmd/
ao/
main.go
internal/
cli/
root.go
start.go
stop.go
status.go
doctor.go
completion.go
version.go
client.go
output.go
process.go
daemon/
daemon.go
Acceptance criteria for the foundation:
go run ./cmd/ao daemonbehaves like today'sgo run ..go run ./cmd/ao startstarts the daemon and waits until/readyzreturns
ready.go run ./cmd/ao status --jsonworks when the daemon is running, stopped, and
stale.go run ./cmd/ao stopgracefully stops the daemon and removesrunning.json.go test ./...,go vet ./..., andgo test -race ./...pass.
Implementation Readiness
This section records what the CLI can connect to in the current codebase and
what still needs to be built. Inventory date: 2026-05-31 on main at
0672dbb.
Implemented Foundation
The daemon-control foundation now exists in backend/cmd/ao and
backend/internal/cli.
Implemented commands:
ao daemonhidden/internal daemon entrypoint.ao startstarts the daemon, waits for/readyz, and supports--json,
--timeout, and--log-file.ao stopstops the daemon fromrunning.json, removes stale run-files, and
supports--jsonand--timeout.ao statusreports stopped/stale/unhealthy/not-ready/ready states and
supports--json.ao doctorchecks config, data dir, SQLite open/migrations, daemon state, and
local tool availability forgit,tmux, andzellij.ao completiongeneratesbash,zsh,fish, andpowershell
completions.ao versionprints build metadata.
The old backend/main.go remains as a compatibility wrapper around
internal/daemon.Run, so go run . still starts the daemon while scripts move
to go run ./cmd/ao ....
Already Implemented and Directly Usable by the CLI
These pieces are available now and are enough to build the daemon-management
part of the CLI.
| Area | Existing code | CLI use |
|---|---|---|
| Daemon config | backend/internal/config loads AO_PORT, AO_REQUEST_TIMEOUT, AO_SHUTDOWN_TIMEOUT, AO_RUN_FILE, and AO_DATA_DIR. Host is fixed to 127.0.0.1. |
ao start, ao daemon, ao status, and ao doctor can share the same config resolution. |
| HTTP server lifecycle | backend/internal/httpd.Server binds loopback, writes running.json, serves until context cancellation, then removes running.json. |
ao daemon can preserve today's daemon behavior after extraction into internal/daemon. |
| Health probes | GET /healthz and GET /readyz. |
ao start can wait for readiness; ao status and ao doctor can check daemon health. |
| Run-file handshake | backend/internal/runfile reads, writes, removes, and stale-checks running.json. |
ao status can discover PID/port; ao stop can find the process; ao start can detect an already-running daemon. |
| Durable store | backend/internal/storage/sqlite opens SQLite, runs goose migrations, uses WAL, stores projects/sessions/PR/check/comment rows, and reads change_log. |
Not directly called by user CLI commands, but confirms the daemon has a durable backend once APIs expose it. |
| CDC substrate | backend/internal/cdc poller and broadcaster exist; daemon starts the poller with startCDC. |
Future ao events tail can build on this once an SSE/API transport exists. |
| Lifecycle manager | backend/internal/lifecycle is implemented and currently wired in daemon startup. |
Session/status APIs can use it; CLI must wait for HTTP routes rather than calling it directly. |
| Reaper timer | backend/internal/observe/reaper exists and is wired. |
Runtime liveness will be available once runtime registry wiring exists. |
Implemented Internally but Not Reachable by CLI Yet
These are real backend components, but the CLI cannot responsibly use them until
they are wired into the daemon and exposed through HTTP.
| Area | Existing code | Missing before CLI can use it |
|---|---|---|
| Project persistence | sqlite.Store has UpsertProject, GetProject, ListProjects, and ArchiveProject. |
Project domain/service layer, project ID/path/origin validation, and /api/v1/projects routes. |
| Session Manager | backend/internal/session.Manager implements Spawn, Kill, Restore, List, Get, Send, and Cleanup. |
Production daemon wiring with real runtime, agent, workspace, messenger, and HTTP routes. |
| Runtime adapters | tmux and zellij adapters implement ports.Runtime and also have attach/send/output helpers. |
Runtime registry wiring in daemon, attach/send abstractions in ports/API, and selection config. |
| Workspace adapter | git worktree adapter implements create/destroy/restore/list with safety checks. | Repo resolver backed by registered projects and daemon wiring into Session Manager. |
| GitHub issue tracker | backend/internal/adapters/tracker/github implements read-only issue Get, List, and Preflight. |
Tracker registry/config, spawn prompt hydration, and project tracker metadata. |
| PR facts storage | SQLite PR/check/comment writes and CDC triggers exist. | SCM/PR observer that fetches GitHub PR/CI/review facts and calls LCM.ApplyPRObservation. |
| Session read model | SessionManager.List/Get derive display status from canonical lifecycle + PR facts. |
HTTP response DTOs and API routes for CLI/frontend reads. |
Still Missing
These are the main gaps before the full initial command set is real.
| Gap | Blocks |
|---|---|
| Cobra dependency and CLI packages. | All CLI commands. |
Daemon extraction from backend/main.go into internal/daemon. |
ao daemon, ao start, tests around daemon startup. |
| CLI process runner and PID signal helpers. | ao start, ao stop. |
| Loopback HTTP client package with run-file discovery. | ao status, later all daemon-backed commands. |
Shutdown mechanism choice: PID signal now, optional POST /api/v1/daemon/shutdown later. |
ao stop polish and cross-platform behavior. |
HTTP API route surface under /api/v1. |
project, spawn, session, send, events list, richer status. |
| SSE route for live CDC events plus durable catch-up reads. | ao events tail, frontend live updates. |
Agent adapters for supported harnesses (codex, claude-code, etc.). |
ao spawn, ao session restore. |
| AgentMessenger implementation over tmux/zellij. | ao send, LCM auto-nudge reactions. |
| Runtime registry wired with tmux/zellij. | Reaper liveness, session attach, spawn/kill/restore runtime work. |
| Notifier implementation/multiplexer. | Human notifications and LCM escalation side effects. |
| Activity hooks or agent self-report protocol. | Accurate working/idle/needs-input status beyond runtime/PR facts. |
| Project/tracker config model. | project add/show, tracker-backed spawn, doctor config checks. |
| OpenAPI/DTO/error contract. | Stable CLI/frontend API clients and tests. |
Command Readiness Matrix
| Command | Can implement now? | Existing support | Remaining work |
|---|---|---|---|
ao daemon |
Implemented | Current daemon startup is extracted to internal/daemon.Run. |
None for foundation. |
ao start |
Implemented | Config, run-file stale check, HTTP readiness probes. | Later: package-manager/service integration if needed. |
ao stop |
Implemented | Run-file discovery gives PID/port; server exits cleanly on SIGINT/SIGTERM. | Optional later shutdown HTTP route. |
ao status |
Partially implemented | Run-file, process liveness via PID, /healthz, /readyz. |
Rich project/session summary waits for /api/v1/projects and /api/v1/sessions. |
ao doctor |
Partially implemented | Config resolution, run-file, storage open, runtime binary checks. | Deeper adapter preflights need daemon wiring/config. |
ao completion |
Implemented | Cobra generators. | None for foundation. |
ao version |
Implemented | Build metadata can be injected with -ldflags. |
Release tooling needs to set metadata. |
ao project list/add/show/remove |
Not yet | SQLite project CRUD exists. | Project service and HTTP routes. CLI must not write SQLite directly. |
ao spawn |
Not yet | Session Manager exists; runtime/workspace/tracker pieces partly exist. | Agent adapters, registry/config wiring, project lookup, tracker hydration, HTTP route. |
ao session list/show |
Not yet | Store and Session Manager read model exist. | HTTP routes and response DTOs. |
ao session attach |
Not yet | tmux/zellij have attach command helpers. | Runtime attach port/API and terminal-launch policy. |
ao session kill/restore |
Not yet | Session Manager implements both. | Production wiring and HTTP routes. |
ao send |
Not yet | Session Manager has Send; tmux/zellij have send helpers. |
AgentMessenger implementation, port/API wiring, busy/idle delivery policy. |
ao events tail/list |
Not yet | Durable change_log, CDC poller, in-process broadcaster. |
SSE route and durable event-list route. |
Recommended Build Order
- Build CLI foundation around the daemon only:
daemon,start,stop,
status,doctor,completion,version. - Add
/api/v1/projectsover a small project service, then implement
project list/add/show/remove. - Wire production Session Manager dependencies: project-backed repo resolver,
tmux/zellij runtime registry, first agent adapter, and AgentMessenger. - Add
/api/v1/sessionsand implementspawn,session list/show/kill/restore,
andsend. - Add
/eventsSSE plus event-list reads, then implementevents tail/list.