Agent and Contributor Guide

July 22, 2026 · View on GitHub

Repo Purpose

This repository contains Agora CLI, the native CLI for Agora developer onboarding. It ships as a single binary with no runtime dependencies and is the primary distribution. The same binary is also published via npm as agoraio-cli (a thin shim that runs the native executable).

Quick Reference

TaskCommand
Build binarygo build -o agora .
Run all testsgo test ./...
Inspect full command tree./agora --help --all
Machine-readable full command tree./agora --help --all --json
Agent introspection artifact./agora introspect --json
Machine-readable outputAdd --json to any command

Source Layout

main.go                     Entry point — wires the root command and calls Execute()
cmd/
  gendocs/                  Regenerates docs/commands.md from the live cobra tree
internal/cli/
  app.go                    App struct, Execute(), output-mode resolver, env snapshot
  commands.go               Root command tree; subcommand builders for auth/config/upgrade/etc.
  envelope.go               JSON envelope shape, exit-code plumbing, error helpers
  render.go                 Pretty output dispatch (renderResult, printBlock, printDoctor)
  paths.go                  Config / session / context paths, writeSecureJSON
  config.go                 appConfig type, defaults, env injection
  version.go                Build-time version vars, versionInfo, formattedVersion
  introspect.go             agora introspect + buildIntrospectionData (agent discovery contract)
  mcp.go                    agora mcp serve — JSON-RPC MCP tool dispatch
  open_targets.go           Canonical URLs for agora open (docs, Console, product docs)
  features.go               Product feature catalog (rtc/rtm/convoai) shared by doctor, introspect, init defaults
  cache.go                  Short-lived on-disk API caches (project list for shell completion)
  completion.go             Dynamic shell completion helpers
  upgrade.go                agora upgrade self-update logic (download, SHA-256, atomic rename)
  progress.go               NDJSON progress event emitter for long-running JSON-mode commands
  auth.go                   login / logout / whoami / auth status
  projects.go               project create / use / show / env / env write / doctor
  quickstart.go             quickstart create / env write / list
  init.go                   init — one-step: project + quickstart + env
  doctor.go                 project doctor — readiness checks, workspace mode
  install_doctor.go         Top-level agora doctor — install self-test (PATH, network, auth, MCP host)
  env_help.go               agora env-help — authoritative env-var catalog
  skills.go                 agora skills — curated workflow recipes (in-binary catalog)
  telemetry.go              telemetryClient interface + noop sink + Sentry placeholder (wire-up scheduled for next release)
  local_project.go          .agora/project.json read/write; repo-local project binding
  runtime_support.go        Template/runtime detection (nextjs, python, go), CI auto-detect, banner rules
  app_test.go               Unit tests for app init and config
  integration_test.go       Integration tests: build binary, shell out, assert JSON
docs/
  automation.md             Stable JSON output contract — machine-consumption source of truth
  install.md                Direct installer, platform, CI, and security guidance
  _config.yml               Jekyll / GitHub Pages configuration (human docs site)
  _layouts/, assets/        Theme assets for Pages
scripts/
  preview-pages-site.sh      Local Jekyll build + URL injection (`make docs-preview`)
  prepare-pages-site.py      Pages artifact prep (Markdown /md mirror, token expansion)
.github/workflows/
  ci.yml                    Push/PR matrix: Ubuntu, macOS, Windows
  release.yml               Tag-driven cross-platform release
  pages.yml                 Publish docs to GitHub Pages
  apt-repo.yml              Signed apt repository publishing

Command Model

The surface is deliberately layered. Use the highest-level command that covers the workflow:

agora
├── init <name>                    Recommended path: reuses existing project (or creates if none); add --new-project to force creation
├── version                        Build version, commit, and date
├── introspect                     Machine-readable command metadata for agents
├── doctor                         Install self-test (PATH, version, network, auth, MCP host); use project doctor for project readiness
├── env-help                       Catalog of every AGORA_* env var the CLI honors
├── skills                         Curated workflow recipes for humans and AI agents (list / show / search)
├── open                           Open Console, CLI docs (human or /md/), or product docs
├── mcp                            Local MCP server for agent tool integrations
├── telemetry                      Telemetry status/enable/disable
├── upgrade (alias: update, self-update)  In-place self-update on installer-managed installs; otherwise prints upgrade guidance
├── project
│   ├── create <name>              Create a remote Agora project (control-plane only)
│   ├── use <name>                 Set global project context
│   ├── show                       Print selected project details
│   ├── env                        Print project env values (no file write)
│   ├── env write <path>           Write a dotenv block to a file
│   └── doctor                     Readiness check; --deep for workspace-level checks
├── quickstart
│   ├── create <name>              Clone an official quickstart repo
│   ├── env write <name|path>      Write the template-specific env file
│   └── list                       List available quickstart templates
├── auth
│   ├── login   (alias: agora login)   OAuth login via browser or manual URL
│   ├── logout                         Clear session
│   └── status  (alias: agora whoami)  Print current session state
└── config
    ├── path    Print the config file path
    ├── get     Print current config values
    └── update  Update a config value

Design rules — do not break these:

  • project = remote Agora control-plane resource; it never scaffolds local files
  • quickstart = local repo clone; requires git on the PATH; upstream .git is removed after clone so demos start without template history
  • init = the only command that composes both
  • The add namespace is reserved; keep it hidden and return a command-not-found error if invoked

Project Resolution Precedence

Commands that need a project resolve context in this order:

  1. Explicit --project flag or positional argument — use in all pipeline and cross-directory operations
  2. Repo-local .agora/project.json — auto-detected from the target directory tree
  3. Global CLI context — set by agora project use

Agent rule: always prefer explicit --project for deterministic, reproducible operations. Use repo-local binding only when operating repeatedly inside a bound quickstart.

Repo-local detection correctly traverses upward from the provided target path argument. Running quickstart env write /abs/path/to/demo from any working directory will find .agora/project.json inside that path.

JSON Output Contract

Every command accepts --json. In automated contexts always use --json — never parse human/pretty output.

./agora init my-demo --template nextjs --json
./agora project doctor --json
./agora auth status --json

The full stable contract with all result shapes is in docs/automation.md.

Envelope:

{
  "ok": true,
  "command": "init",
  "data": { ... },
  "meta": { "outputMode": "json", "exitCode": 0 }
}
FieldStableNotes
okyesBranch on this first
commandyesStable command label
datayesnull on failure
error.messageyesPresent on failure
error.codeyesPresent on known structured failures
meta.outputModeyesAlways "json"
meta.exitCodeyesProcess exit code for success and failure

auth status, whoami, and API-touching commands return exit code 3 plus ok: false with error.code == "AUTH_UNAUTHENTICATED" when no local session exists. Treat that as the unauthenticated state and run agora login before commands that require a session.

Set AGORA_AGENT=<tool-name> in agent runs to explicitly label API requests in User-Agent. If it is unset, the CLI infers a coarse label from known agent environment markers (Cursor, Claude Code, Cline, Windsurf, Codex, Aider) unless AGORA_AGENT_DISABLE_INFER=1 is set.

Testing

go test ./...
  • app_test.go — unit tests for app initialization and config
  • integration_test.go — builds the binary, shells out, asserts JSON output shapes

Linting

make lint            # gofmt + golangci-lint + error-code audit
golangci-lint run    # standalone (config: .golangci.yml)

CI uses golangci-lint v2.12.2 through the v9 action. Install locally to match:

go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2

Alternatively, download the release binary (must be built with a Go version ≥ go.mod; if config load fails, prefer go install above):

curl -sSfL https://golangci-lint.run/install.sh \
  | sh -s -- -b "$(go env GOPATH)/bin" v2.12.2

The ruleset is intentionally conservative (errcheck, govet, staticcheck, ineffassign, unused, gosec, bodyclose, errorlint, misspell, unconvert). When a finding is a false positive, prefer narrowing the rule via .golangci.yml exclude-rules over adding inline //nolint directives.

When adding a command:

  1. Register it in commands.go
  2. Add a happy-path JSON test in integration_test.go
  3. Add edge-case unit tests in app_test.go for non-trivial logic

Adding a New Command

  1. Create internal/cli/<noun>.go with business logic on *App
  2. Register the command in commands.go inside buildRoot()
  3. Accept --json via a.resolveOutputMode(cmd); return results through renderResult(cmd, "command label", data)
  4. Add the command to the README command model
  5. Add a stable JSON result shape to docs/automation.md
  6. Call out breaking JSON or exit-code changes in CHANGELOG.md and migration notes in docs/automation.md when behavior is intentional.
  7. If the command is exposed through MCP, update mcpTools() and refresh the compact tool-surface summary in docs/llms.txt.

CI and Release

WorkflowTriggerWhat it does
ci.ymlpush, PRgo test ./... + gofmt, golangci-lint, error-code coverage audit on Ubuntu, macOS, Windows
release.ymlv* tagBuilds cross-platform binaries, publishes GitHub release and package channels
apt-repo.ymlpublished releaseUpdates the signed apt repository

Tagging v0.2.1 (or any v* semver tag) triggers the release workflow automatically.

Gotchas

IssueDetail
git requiredquickstart create and init shell out to git clone
Headless OAuthUse --no-browser to print a URL instead of opening a browser
quickstart env writeproject env writeTemplate-aware paths and variable names vs generic dotenv block
add namespaceReserved and hidden; must behave as not-found from the user's perspective
doctor --deepStable workspace checks for .agora metadata and quickstart env consistency; prefer --deep --json in repo-bound automation.
open browser launchAuto-open happens only in interactive pretty TTY sessions outside CI. Use --browser to force opening or --no-browser for URL-only behavior.
App certificate requiredquickstart env write and init fail env injection if the project has no certificate

npm Distribution (Node Wrapper)

The Go binary is also distributed via npm as agoraio-cli. The packaging lives entirely in this repo under packaging/npm/.

Structure:

packaging/npm/
  agoraio-cli/              ← the published npm package (Node shim only)
    bin/agora.js            ← entry point: resolves platform binary and spawns it
    package.json            ← optionalDependencies for all 6 platforms
  agoraio-cli-darwin-arm64/ ← one unscoped package per platform
  agoraio-cli-darwin-x64/
  agoraio-cli-linux-arm64/
  agoraio-cli-linux-x64/
  agoraio-cli-win32-x64/
  agoraio-cli-win32-arm64/
    package.json            ← os/cpu fields restrict install to matching platform
    bin/                    ← .gitignored; populated by CI at release time

How it works:

  1. npm install -g agoraio-cli installs the shim + the matching platform package via optionalDependencies
  2. bin/agora.js resolves agoraio-cli-<platform>/bin/agora and spawnSyncs it with all args inherited
  3. If the platform package is missing, the shim prints a helpful error pointing to Homebrew or GitHub releases

Release flow (automated and active): the publish-npm job in release.yml:

  1. Downloads release archives (agora-cli_v*, v0.2.1+) and checksums.txt from the GitHub release
  2. Verifies SHA-256 of every archive against checksums.txt; fails the job on mismatch
  3. Extracts the binary for each platform into the corresponding package's bin/
  4. Stamps the tag version into all package.json files (wrapper + 6 platform packages, including optionalDependencies values)
  5. Publishes all 6 platform packages with NPM_TOKEN, then publishes the wrapper package with npm publish --provenance via npm trusted publishing
  6. Smoke-tests the published wrapper with npx --yes agoraio-cli@<tag> --version (retry/backoff for registry propagation)

Prerequisites:

  • npm Trusted Publisher configured on agoraio-cli, pointing at repo AgoraIO/cli and workflow release.yml.
  • NPM_TOKEN GitHub secret with publish access to all six agoraio-cli-* platform packages.
  • id-token: write workflow permission (already set in release.yml) — required for wrapper trusted publishing and provenance.

Manual dry-run: the workflow exposes workflow_dispatch with a dry_run input that runs npm publish --dry-run against a synthetic version, validating packaging without publishing.

Installing from npm (users):

npm install -g agoraio-cli   # installs shim + native binary for current platform
npx agoraio-cli --help       # or via npx without global install

The shell installer remains the primary install mechanism. npm is a convenience path for developers already in a Node.js ecosystem and benefits from the supply-chain provenance attestations attached at publish time.