README
¶
agent-cli-generator
Generate agent-first CLIs from OpenAPI specs.
Builds a regular, low-ambiguity interface for LLMs: canonical operation IDs, JSON in/out, schema introspection, local validation, dry-run planning, and a single skill that teaches the agent everything it needs.
Install
curl -fsSL https://raw.githubusercontent.com/jescalan/agent-cli-generator/main/scripts/install.sh | sh
Or via Homebrew, Go, or source:
brew install jescalan/tap/agent-cli-generator # Homebrew
go install github.com/jescalan/agent-cli-generator@latest # Go
go build . # source
Quick start
1. Create a config file
# agent-cli.yml
spec: ./openapi.yaml
publish: acme/myapi-cli
That's the minimum. The generator infers name from the spec title, module from the publish repo, and defaults output to ./<repo-name>. It also defaults overwrite to true, which makes regeneration easy while still refusing to overwrite directories it did not create.
Optional fields:
name: myapi # override the binary name
module: github.com/acme/myapi-cli # override the Go module path
output: ./out # generate into a different directory
homebrew_tap: acme/homebrew-tap # enable Homebrew publishing
build: true # produce a native binary
overwrite: false # disable regeneration into the same output dir
2. Generate
agent-cli-generator generate
Or skip the config file and pass flags directly:
agent-cli-generator generate \
--spec ./openapi.yaml \
--output ./out/myapi-cli \
--publish acme/myapi-cli \
--build
3. Publish
The generated project is a standalone Git repo. Push it to the GitHub repository you specified in publish:
cd myapi-cli
git init && git add -A && git commit -m "initial generation"
git remote add origin git@github.com:acme/myapi-cli.git
git push -u origin main
git tag v0.1.0 && git push origin v0.1.0
The first tag push triggers the included release workflow, which runs GoReleaser to produce cross-platform binaries, checksums, and a GitHub release. If you set homebrew_tap, it also publishes a Homebrew formula (requires a HOMEBREW_TAP_GITHUB_TOKEN secret with push access to the tap repo).
After the initial push, the included regenerate workflow handles ongoing updates from the checked-in openapi.json: when you commit spec changes to main, it regenerates the CLI in a temporary directory, syncs the result back into the repo, commits, tags a patch bump, and pushes — which triggers the release workflow. No manual tagging is needed after the first release.
4. Your users install one skill
npx skills add https://github.com/acme/myapi-cli
That single skill bootstraps the CLI binary, teaches the agent the schema-first workflow, lists auth requirements, and catalogs every operation. No further setup needed.
Status
Usable today for JSON-first APIs. Exercised against real specs from Clerk, Tailscale, Supabase, Fly Machines, Resend, GitHub, Nylas, Customer.io, Kinde, and YNAB.
How it works
The generated CLI exposes six core commands:
operations # list all operations
schema <id> # inspect inputs, outputs, auth
example <id> --kind body|params|response # concrete payload shape
api <id> --params '{...}' --body '{...}' # make a request
auth # show required env vars
openapi # dump the embedded OpenAPI doc
When the source API exposes an obvious self-identity endpoint like /whoami or /me, the generated CLI also adds:
whoami # call the detected identity endpoint
Params use location-aware JSON: {"path": {}, "query": {}, "header": {}, "cookie": {}}. Flat params work when a name is unambiguous.
Auth is environment-driven. OAuth2 client_credentials flows get native token acquisition. Everything else uses bearer tokens or API keys via generated env vars.
Reference
Generator flags
| Flag | Description |
|---|---|
--spec |
Path or URL to an OpenAPI 3.0/3.1 or Swagger 2.0 spec |
--output |
Output directory (default ./<repo-name> when publish is set, otherwise ./generated) |
--name |
Binary name (default: derived from spec title) |
--module |
Go module path (default: inferred from --publish) |
--publish |
GitHub owner/name where the generated CLI will be published |
--repo |
Deprecated alias for --publish |
--homebrew-tap |
Homebrew tap owner/name for formula publishing |
--build |
Run go build after generation |
--overwrite |
Allow writing into an existing directory (default true with config file) |
Incomplete specs
Use *_HEADERS_JSON for undeclared headers (e.g. version pinning):
CLERK_HEADERS_JSON='{"Clerk-API-Version":"2025-11-10"}' clerk api GetUsersCount
Use *_OVERRIDES_JSON when the spec is missing auth or has conditional input rules:
export FLY_API_TOKEN=...
export FLYMACHINES_OVERRIDES_JSON='{"auth":{"headers":[{"name":"Authorization","env":"FLY_API_TOKEN","prefix":"Bearer ","required":true,"secret":true}]}}'
flymachines api apps.list --params '{"query":{"org_slug":"personal"}}'
The override layer also supports per-operation requirements for conditional params.
Current limits
- JSON APIs are the best-tested path. Multipart, binary upload/download need more hardening.
- Native token acquisition covers OAuth2
client_credentialsonly. Other flows require pre-minted tokens. - Pagination is heuristic — works when the spec exposes a common token-based contract.
- Swagger 2.0 inputs are converted to OpenAPI 3 before generation.
- The loader strips bad examples/defaults if they are the only validation failures.
Development
go test ./...
go test -race ./...
go vet ./...
Most behavior lives under internal/generator. The test suite leans heavily on end-to-end tests that build and execute generated CLIs.
See CONTRIBUTING.md and AGENTS.md for contributor and AI agent guidance.
License
MIT. See LICENSE.
Documentation
¶
There is no documentation for this package.