Thanks for your interest in improving the Agora CLI! This document covers the quick path: how to set up, what to run before opening a PR, where to find more detailed guidance, and what we expect from contributions.
This project adheres to the Contributor Covenant. By participating, you agree to uphold its standards. Please report unacceptable behavior to devrel@agora.io.
For day-to-day development, the most important reference is AGENTS.md in the repo root. It documents:
- The CLI's design contracts (JSON envelopes, exit codes, error code catalog).
- Project layout (
internal/cli/,cmd/,docs/,packaging/). - How to add a new command end-to-end (registration, JSON shape, tests, docs).
- The CI/release pipeline.
- The lint and test workflow.
AGENTS.md is the contract for both human contributors and AI coding agents,
so it is kept current as the canonical engineering guide.
For end-user behavior and machine-readable contracts, see:
README.md— install, getting-started, command tree.- Published docs — human-readable CLI documentation (GitHub Pages).
docs/automation.md— JSON envelope, agent guidance, output mode precedence (including CI auto-detect).docs/error-codes.md— every stableerror.code.docs/install.md— installer / package channel matrix.
Requirements:
- Go 1.26.2+ (see
go.mod). Release builds intentionally track the current stable Go toolchain; this distributed CLI does not target older Go compiler support. - Git.
- (Optional)
golangci-lintv1.64.8 — install matches CI; instructions in the next section.
git clone https://github.com/AgoraIO/cli.git
cd cli/
go build -trimpath -o agora .
./agora --helpRun the full local check suite before opening a PR:
make test # go test ./...
make lint # gofmt + golangci-lint + error-code coverage auditOr run pieces individually:
go test ./... # unit + integration tests
gofmt -l . # must print nothing
golangci-lint run --timeout=5m # uses .golangci.yml
./scripts/check-error-codes.sh # docs/error-codes.md drift check
go run ./cmd/gendocs -check # docs/commands.md drift check
make docs-preview # optional: local Jekyll site + /md preview (requires Ruby/Jekyll)Documentation work:
- Run
make docs-commandsafter command-tree changes; CI usesgo run ./cmd/gendocs -check. - For GitHub Pages content, use
make docs-preview(seescripts/preview-pages-site.sh). Published docs resolve@@CLI_DOCS_*@@and@@CLI_INSTALL_*@@tokens viascripts/prepare-pages-site.pyandinternal-docs/pages/site.envas documented indocs/automation.md.
Install golangci-lint v1.64.8 (matches CI). CI builds it with go install against your toolchain; locally prefer:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.8Or use the install script if the binary supports your go.mod Go version.
When changing release packaging, also run a snapshot release:
make release-snapshot # goreleaser release --snapshot --clean- Create
internal/cli/<noun>.gowith business logic on*App. - Register the command in
commands.goinsidebuildRoot(). - Use
a.resolveOutputMode(cmd)to honor--json/--output/ CI auto-detect. - Return results through
renderResult(cmd, "<command label>", data). - Document the JSON shape and any new
error.codevalues indocs/automation.md. - Run
make docs-commandsto refreshdocs/commands.md. CI fails if it drifts. - Add a happy-path JSON test in the appropriate
internal/cli/integration_*_test.gofile (helpers live inintegration_test.go). - Add edge-case unit tests in
internal/cli/app_test.gofor non-trivial logic. - If a new
error.codewas introduced, add it todocs/error-codes.md. Themake lintaudit will fail otherwise.
Every literal Code: value emitted from internal/cli/*.go must be
documented in docs/error-codes.md. Dynamic prefixes (e.g. FEATURE_<NAME>_PROVISIONING)
must have a documented prefix entry. CI runs scripts/check-error-codes.sh
on every PR.
Codes are part of the public CLI contract. Renaming a code is a breaking change; prefer adding a new code and deprecating the old one over a rename.
- Formatting:
gofmt— enforced in CI. - Lint:
golangci-lintwith the project.golangci.yml. Prefer narrowing rules in the config file over inline//nolintdirectives. - Errors: wrap with
%w(fmt.Errorf("doing X: %w", err)); useerrors.As/errors.Israther than type assertions. - JSON shapes: the JSON envelope (
ok,command,data,error,meta) is a public contract. Any breaking change must be called out in the PR description and the changelog. - Logging: use
appendAppLogfor structured logs. Never log secrets — thesensitiveFieldPatternredactor catches obvious cases but is not a substitute for thinking about what you're emitting. - Tests: prefer integration tests in
integration_test.gofor behavior that is part of the public contract (JSON shape, exit code, stderr text). Useapp_test.gofor isolated helper logic.
GitHub Actions are configured for:
- push and pull request validation on Linux, macOS, and Windows
- automated tag-driven releases for
v*tags - cross-platform release artifacts for Linux, macOS, and Windows
Release workflow behavior:
- a pushed tag matching
v*(for examplev0.2.5) triggers the release workflow - the workflow runs tests, builds release binaries, packages them, and publishes a GitHub release automatically
- release artifacts include checksums, Cosign signatures, and an SBOM
See AGENTS.md for the full release pipeline (npm, Homebrew, apt, GitHub Pages).
mainis always releasable. CI must be green before merge.- Feature work happens on short-lived topic branches off
mainnamedfeat/<scope>,fix/<scope>,docs/<scope>, orchore/<scope>(matches the conventional-commits prefixes). Avoid long-running branches; rebase onmaininstead of merging it back into your topic branch. - Releases are cut from
mainby taggingvX.Y.Z. The release workflow handles building, signing, publishing, and Homebrew / Scoop / npm bumps. See docs/install.md for the release matrix.
- Keep commits focused. One logical change per commit is preferred.
- Write present-tense imperative subjects ("Add CI auto-detect", not
"Added CI auto-detect"). We prefer (but do not strictly require)
the Conventional Commits
prefixes (
feat:,fix:,docs:,chore:,refactor:,test:,build:). - Reference issues with
Fixes #123/Refs #456in the body when relevant. - We do not require DCO sign-off
today, but contributors are welcome to sign their commits with
git commit -s. If we adopt mandatory sign-off in the future, we will announce it here and add a CI check.
- Fill in the pull request template.
- Make sure
make test && make lintpass locally. - Include changelog entries under the
## [Unreleased]section ofCHANGELOG.mdfor user-facing changes (new commands, behavior changes, breaking changes, CLI exit code changes, error code additions). When cutting a release, move those bullets into a dated## [x.y.z] - YYYY-MM-DDsection per the note at the top ofCHANGELOG.md(for example, v0.2.0 shipped as## [0.2.0] - 2026-05-05). - For UI/UX-affecting changes (pretty output, prompts, progress events, errors), include before/after copy-paste samples in the PR description.
- New commands MUST include a per-command example block in the Cobra
Example:field. See "Adding a new command" above and the existingagora skills,agora doctor, andagora env-helpbuilders for the current style.
Use the GitHub issue templates:
For support (questions, "how do I", install help) see SUPPORT.md.
For security issues, see SECURITY.md. Email security@agora.io rather than filing a public issue. Do not include credentials or App Certificates in any public report.
By contributing, you agree that your contributions will be licensed under the project's MIT license.