Auto-generate agent-ready CLIs from any codebase in any language. Reads your project's source code — MCP servers, OpenAPI specs, Next.js, Express, Flask, Go, Rails, gRPC, GraphQL, or any API — and generates a complete TypeScript CLI with --json output, dual TTY/JSON mode, auth profiles, doctor command, and esbuild bundling. Use when asked to 'generate a CLI', 'create a CLI for this project', 'make this agent-ready', or 'add a CLI'.
Generate a complete, production-grade CLI from any web/SaaS codebase in 5 phases.
Quick start: Run /cli-generate inside any project directory. It auto-detects the project type and generates a CLI.
What it generates:
doctor command for diagnosticswhoami command for auth status.cjs bundle via esbuildnpx instant execution readyScan the codebase to determine what kind of project this is. Read references/detection-matrix.md for the full detection logic.
Two-tier detection:
Tier 1 (pattern-based): Check for known frameworks first — MCP SDK, OpenAPI specs, Next.js routes, Express/Fastify. These have mechanical extraction rules.
Tier 2 (LLM-native): If Tier 1 doesn't match, READ THE CODE. Claude can understand any language — Python Flask, Go Gin, Ruby Rails, Rust Actix, gRPC protos, GraphQL schemas. Scan entry points, find route handlers/endpoints/RPC definitions, and extract the API surface.
Launch parallel searches:
Output: Detection report with:
Present findings to user. Asking the user to describe the API is the LAST resort — only if Claude genuinely cannot find endpoints after reading the code.
Map each detected endpoint/tool to a CLI command. Read references/command-patterns.md for mapping rules.
Generate a plan:
CLI: {name}-cli
Commands:
{name}-cli login — Authenticate with API key
{name}-cli logout — Remove credentials
{name}-cli whoami — Show auth status
{name}-cli doctor — Run diagnostic checks
{name}-cli {command-1} — {description}
{name}-cli {command-2} — {description}
...
Global flags: --json, --quiet, --api-key, --profile, --verbose
Auth: {API_KEY_ENV_VAR} env var + ~/.config/{name}-cli/credentials.json
Naming rules:
list-users, send-email, mrr)users list, users get, users create)Present plan to user. Wait for approval before generating code.
Generate the fixed boilerplate files. These are identical for every CLI — only names change. Read references/cli-architecture.md for the exact file contents.
Files to generate:
package.json — deps: commander, @commander-js/extra-typings, @clack/prompts, picocolors, esbuild (dev), tsx (dev), typescript (dev), @types/node (dev), plus any project-specific SDKtsconfig.json — ES2022, bundler moduleResolution, strictbuild.mjs — esbuild bundle to single dist/cli.cjs with shebang.gitignore — node_modules, dist, .env, *.tgz, .DS_StoreLICENSE — MITsrc/lib/)constants.ts — VERSION, CLI_NAME, CONFIG_DIR_NAME, USER_AGENTconfig.ts — XDG config, profiles, auth chain (flag > env > file), GlobalOptsoutput.ts — ExitCode enum, shouldOutputJson, outputResult, outputError, outputFormattedtable.ts — hand-rolled column-aligned table renderertty.ts — isInteractive() terminal detectionspinner.ts — braille spinner for async operationsformat.ts — CSV and Markdown export formattersbanner.ts — ASCII art banner (generated via npx figlet-cli)Customization points (change per project):
constants.ts: CLI_NAME, CONFIG_DIR_NAMEconfig.ts: env var names (e.g., {NAME}_API_KEY, {NAME}_PROFILE), GlobalOpts fieldsbanner.ts: ASCII art — always generate with npx figlet-cli -f "ANSI Shadow" "{cli-name}". Never hand-draw ASCII art.package.json: name, description, keywords, repository, project-specific dependenciesAfter scaffolding, run npm install and npm run build to verify.
For each endpoint in the plan, generate a command file in src/commands/. Read references/command-patterns.md for the exact patterns per project type.
Every command follows this pattern:
import { Command } from '@commander-js/extra-typings'
import type { GlobalOpts } from '../lib/config.js'
import { resolveApiKey } from '../lib/config.js'
import { shouldOutputJson, outputError, ExitCode } from '../lib/output.js'
import { withSpinner } from '../lib/spinner.js'
export function make{Name}Command(globalOpts: () => GlobalOpts): Command {
return new Command('{command-name}')
.description('{description}')
// Add command-specific options from endpoint params
.action(async (cmdOpts) => {
const opts = globalOpts()
const apiKey = resolveApiKey(opts)
if (!apiKey) {
outputError({ code: 'AUTH', message: 'No API key. Run `{cli-name} login` or set {ENV_VAR}.' }, opts)
process.exit(ExitCode.AUTH_ERROR)
}
try {
// Fetch data with spinner
const result = await withSpinner('Fetching...', () => callApi(apiKey, cmdOpts), opts)
// JSON mode
if (shouldOutputJson(opts)) {
process.stdout.write(JSON.stringify(result, null, 2) + '\n')
return
}
// Human output (tables, formatted text, etc.)
// ...
} catch (err) {
outputError({ code: 'API', message: err instanceof Error ? err.message : 'Unknown error' }, opts)
process.exit(ExitCode.API_ERROR)
}
})
}
Also generate:
src/commands/login.ts — interactive auth with @clack/promptssrc/commands/logout.ts — remove credentialssrc/commands/whoami.ts — show auth statussrc/commands/doctor.ts — CLI version, Node.js version, API key check, connection testsrc/index.ts — Commander program with all global options, register all commandsAlso generate:
src/core/client.ts — API client (HTTP fetch wrapper or SDK instantiation)src/core/types.ts — TypeScript interfaces for API responsesAfter generating, run npm run build and npx tsc --noEmit to verify.
Run the verification checklist:
npm run build — clean build, produces dist/cli.cjsnode dist/cli.cjs --version — prints versionnode dist/cli.cjs --help — shows all commandsnode dist/cli.cjs (no args) — shows banner + helpnode dist/cli.cjs doctor --json — outputs valid JSONls -lh dist/cli.cjs (target: <500 KB)Generate SKILL.md for AI agent discoverability:
Generate README.md using the template in references/cli-architecture.md:
banner.ts (plain text, no ANSI).description() stringsFix any build/type errors. Present the final CLI to the user.
z.array(z.string()) needs special handling — use --items item1,item2 with .split(','). z.object() needs --data '{json}' with JSON.parse().operationId. Skip paths without it. If >30 commands, group by tag into subcommands.accounts.retrieve() exists — find the lightest endpoint to validate the key.picocolors uses ANSI codes that break padEnd(). Never call .padEnd() on colored strings. Pad first, then color..js extensions. Every import must end with .js even though source is .ts. This is required by module: ES2022.process.stdout.isTTY is undefined when piped. shouldOutputJson checks this — never skip it.npx figlet-cli -f "ANSI Shadow" "{name}" via Bash and paste the output into banner.ts as a hardcoded string. See references/cli-architecture.md for the pattern.--json support. No exceptions.npm run build and fix errors.