CLI Development

The Go CLI under cli/ wraps the public API as seed <resource> <verb>. It's used by humans, by deploy smoke, and most importantly by the Pi agent inside sandboxed tasks — Pi discovers commands at runtime, so CLI coverage is what determines what tasks can do.

Default stance

Spec first, CLI follows. Every CLI command should map to one or more OpenAPI endpoints. If the API doesn't exist yet, go to add-api-endpoint first — adding a CLI command without a spec entry creates a feature that has no SDK and no MCP exposure.

Non-interactive by default. All commands must work with --flag args or env vars; never block on stdin for required values. The CLI runs inside Pi sandboxes (no TTY), in CI, and in deploy smoke. An interactive prompt breaks all three.

Human-readable default, JSON on demand. Commands print pretty output by default and switch to --output json when needed. Pi parses JSON; humans read tables. Don't pick one and lose the other.

Use this skill when

Adding a CLI command for a new API endpoint, changing CLI auth/config resolution, fixing CLI tests, rebuilding cli/bin/seed for local smoke, or auditing CLI coverage after API changes.

Workflow

  1. Inspect the API surface — read openapi/v1.yaml and app/api/v1/<resource>/route.ts for what's available.
  2. Check existing patterns — look at cli/cmd/keys.go or cli/cmd/usage.go for the conventional shape (Cobra command, flags, output formatting).
  3. Add the commandcli/cmd/<resource>.go with both query and mutation subcommands as needed.
  4. Add testscli/cmd/<resource>_test.go next to the source.
  5. Rebuild the binarycd cli && make build. Stale cli/bin/seed is the most common cause of "deploy smoke fails but the deployed API works."
  6. Run Go testscd cli && go test ./....
  7. Run repo gates./scripts/gates.sh.

Conventions

  • Cobra commands — one resource per file (cli/cmd/<resource>.go). Subcommands: list, get, create, update, delete, plus resource-specific verbs.
  • Auth resolution — API key from --api-key flag → SEED_API_KEY env → config file at ~/.seed/config.json. The auth command sets the config.
  • Base URL--api-url flag → SEED_API_URL env → default to production URL from lib/brand.ts.
  • Output — pretty default, --output json flag for machine consumption.
  • Errors — non-zero exit code, error message to stderr, no panics on user errors.

Pi consumption pattern

Pi is the heaviest user of the CLI. Every command should be safe for Pi to run:

  • No interactive prompts. --api-key and --organization-id should always be flag-passable.
  • Idempotent reads. seed keys list shouldn't have side effects.
  • Predictable JSON output. Pi often filters with jq. Field names should be stable and documented in the command help.
  • Clear exit codes. Non-zero on failure so Pi can detect and react.

Hard rules

  • Don't add a CLI command for an endpoint that doesn't exist in openapi/v1.yaml. Spec first.
  • Don't break existing flag names in a non-major release. The CLI is consumed by automated systems that won't notice silent renames.
  • Don't ship without rebuilding cli/bin/seed if you're going to run deploy smoke afterward. The smoke runner uses the local binary.
  • Don't put business logic in the CLI. It's a thin wrapper over the API. If the CLI computes something the API doesn't, that's an API gap to fix, not a CLI feature.

Where things live

FilePurpose
cli/cmd/root.goCobra root, global flags, version
cli/cmd/auth.goseed auth login — writes config
cli/cmd/keys.go, keys_test.goAPI key management — reference pattern
cli/cmd/me.goseed me — current user/org
cli/cmd/usage.goUsage queries
cli/cmd/version.goseed version
cli/Makefilemake build produces cli/bin/seed
cli/AGENTS.mdCLI development patterns and conventions
cli/bin/seedBuilt binary used by smoke (gitignored)
docs/cli.mdCLI architecture overview

Auxiliary content