CLI Generator
SPEC → Bun CLI. Production-ready. Autocomplete, --help, token-aware output.
Quick Start
/cli-generator from-spec <spec-file> — OpenAPI / JSON Schema → CLI
/cli-generator from-api <url> — live API → CLI (probe endpoints)
/cli-generator from-description "<text>" — natural language → CLI
/cli-generator validate <cli-dir> — check generated CLI against spec
Input Sources
| Source | Example | Best for |
|---|---|---|
| OpenAPI 3.x | openapi.yaml, swagger.json |
REST APIs |
| JSON Schema | schema.json |
Data tools, validators |
| GraphQL SDL | schema.graphql |
GraphQL APIs |
| Markdown spec | SPEC.md |
Feature-driven design |
| NL description | "CLI to manage user accounts with CRUD" |
Rapid prototyping |
| Live API URL | https://api.example.com |
Reverse-engineering |
Output — What You Get
my-cli/
├── package.json # bin entry, dependencies
├── tsconfig.json # Bun-optimized TS config
├── src/
│ ├── index.ts # entry point, commander setup
│ ├── commands/ # one file per subcommand
│ │ ├── list.ts
│ │ ├── get.ts
│ │ ├── create.ts
│ │ ├── update.ts
│ │ └── delete.ts
│ ├── lib/
│ │ ├── client.ts # typed API client (generated)
│ │ ├── format.ts # output formatters (json, table, yaml)
│ │ └── config.ts # env vars, config file handling
│ └── types.ts # generated from spec
├── completions/
│ ├── bash.sh
│ ├── zsh.sh
│ └── fish.sh
├── man/
│ └── my-cli.1 # man page
├── test/
│ └── commands.test.ts # bun test suite
├── README.md # usage docs
└── Makefile # build, install, test
Workflow — Spec to CLI
Claude will:
Phase 1: Parse
- Read the input (OpenAPI, JSON Schema, markdown, or NL)
- Extract: commands, subcommands, flags, args, types
- Infer missing details (descriptions, defaults, validation rules)
- Confirm the command tree with user before generating
Phase 2: Generate
- Scaffold project structure with
package.json+tsconfig.json - Generate typed API client from spec (if OpenAPI/GraphQL)
- Generate one file per subcommand with commander/args
- Wire up autocomplete (bash/zsh/fish)
- Generate man page
- Generate test suite with
bun test
Phase 3: Verify
bun run src/index.ts --help— validates CLI bootsbun test— all generated tests passbun run build— compiles without errors- Output: ready to
npm install -gorbun link
Generated Code Example
Input — OpenAPI snippet:
paths:
/users:
get:
operationId: listUsers
summary: List all users
parameters:
- name: role
in: query
schema:
type: string
enum: [admin, user, guest]
- name: limit
in: query
schema:
type: integer
default: 20
- name: offset
in: query
schema:
type: integer
default: 0
post:
operationId: createUser
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name, email]
properties:
name:
type: string
email:
type: string
format: email
role:
type: string
enum: [admin, user, guest]
default: user
/users/{id}:
get:
operationId: getUser
summary: Get user by ID
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
Output — src/commands/list.ts:
import { Command } from 'commander'
import { client } from '../lib/client'
import { formatOutput } from '../lib/format'
import type { User } from '../types'
export const listUsersCommand = new Command('list')
.description('List all users')
.option('--role <role>', 'Filter by role (admin, user, guest)')
.option('--limit <number>', 'Max results', '20')
.option('--offset <number>', 'Pagination offset', '0')
.action(async (opts) => {
const { data, error } = await client.GET('/users', {
params: {
query: {
role: opts.role,
limit: Number(opts.limit),
offset: Number(opts.offset),
},
},
})
if (error) {
console.error(`Error: ${error.message}`)
process.exit(1)
}
formatOutput<User[]>(data, {
columns: ['id', 'name', 'email', 'role'],
format: opts.format ?? 'table',
})
})
Bundled Runtime — Token-Aware Output
Every generated CLI includes src/lib/format.ts with three modes:
| Flag | Mode | Use case |
|---|---|---|
--format json |
Structured | Piped to jq, scripts |
--format table |
Human-readable | Terminal viewing |
--format tokens |
Token-optimized | AI harness consumption |
--format tokens strips whitespace, deduplicates, and outputs only essential fields — minimizing context window consumption when a CLI is invoked by an AI agent.
Validation
/cli-generator validate ./my-cli
Checks:
- ✓ All spec endpoints have corresponding commands
- ✓ All required params are enforced
- ✓ Types match between spec and generated code
- ✓ Autocomplete covers all flags
- ✓ Tests exist for every command
- ✓ Build succeeds
- ✓ --help output is complete
Frameworks Used
- Runtime: Bun — fast startup, native TS, built-in test runner
- Arg parsing:
commander(most mature) orcitty(lighter, Bun-native) - API client: generated
openapi-fetchwrapper (typed) orfetchwith Zod validation - Output: custom
--format tokensmode for AI harness consumption
Design Decisions
- One file per command — keeps files small, AI-navigable
- Typed client from spec — no manual fetch calls, full autocomplete
- Token-aware by default — every CLI ships with
--format tokens - Tests generated alongside code — not an afterthought
- Shell completions built-in — no extra install step
Example Session
User: /cli-generator from-spec ./openapi.yaml
Claude:
✓ Parsed OpenAPI 3.1 — 12 endpoints, 5 schemas
Command tree:
users list [--role] [--limit] [--offset]
users get <id>
users create --name --email [--role]
users update <id> [--name] [--email] [--role]
users delete <id>
Confirm? (y/n)
User: y
Claude:
✓ Scaffolded project (8 files)
✓ Generated typed client
✓ Generated 5 command files
✓ Generated autocomplete (bash/zsh/fish)
✓ Generated man page
✓ Generated 12 tests
✓ Build passing
✓ CLI boots: my-cli --help
Done. Try:
cd my-cli && bun link && my-cli users list --format tokens