Generate a working CLI from any API, then wrap it in a Claude Code skill. Point it at API docs, a live URL, or a peek-api capture and get a dual-mode Commander.js CLI (human + agent output) plus a ready-to-use skill folder. Use when user wants to wrap an API in a CLI, generate a CLI from API docs, turn an API into a command-line tool, scaffold a CLI from discovered endpoints, or create a skill for an API.
Install via CLI
openskills install alexknowshtml/api2cli---
name: api2cli
description: Generate a working CLI from any API, then wrap it in a Claude Code skill. Point it at API docs, a live URL, or a peek-api capture and get a dual-mode Commander.js CLI (human + agent output) plus a ready-to-use skill folder. Use when user wants to wrap an API in a CLI, generate a CLI from API docs, turn an API into a command-line tool, scaffold a CLI from discovered endpoints, or create a skill for an API.
---
# api2cli
Generate a working Node.js CLI from any API, then wrap it in a Claude Code skill. Discovers endpoints, scaffolds a dual-mode Commander.js CLI with a full-featured API client, and creates a skill folder so Claude knows how to use it.
## Workflow
1. **Identify the API** -- user provides a docs URL, a live API base URL, or a peek-api capture
2. **Discover endpoints** -- parse docs, probe the API, or read a peek-api catalog
3. **Build endpoint catalog** -- normalize all discovered endpoints into a standard format
4. **Generate CLI** -- scaffold Commander.js CLI from the catalog
5. **User chooses destination** -- scaffold into current project or create standalone project
6. **Generate skill** -- create a SKILL.md that teaches Claude how to use the generated CLI
## Step 1: Identify the API
Ask the user:
- "What API do you want to wrap? Share a docs URL, a base URL, or point me at a peek-api capture."
Determine which discovery paths to use based on what they provide:
| Input | Discovery Path |
|-------|---------------|
| Docs URL (e.g., `https://docs.stripe.com/api`) | Docs parsing + active probing |
| Base URL (e.g., `https://api.example.com/v1`) | Active probing |
| peek-api capture dir (e.g., `./peek-api-linkedin/`) | Read existing catalog |
| Live website URL | Suggest running peek-api first, then active probing |
Also ask:
- "What auth does this API use?" (API key, Bearer token, cookies, OAuth, none)
- "Do you want this CLI in your current project or as a standalone project?"
## Step 2: Discover Endpoints
Use all applicable discovery paths. Combine results into a single catalog.
### Path A: Docs Parsing
1. Fetch the docs URL with WebFetch
2. Extract endpoint information: method, path, description, parameters, request/response examples
3. Look for pagination patterns, auth requirements, rate limit info
4. Follow links to sub-pages for individual endpoint docs if the main page is an index
### Path B: Active Probing
1. Check well-known paths for API specs:
- `/.well-known/openapi.json`, `/.well-known/openapi.yaml`
- `/openapi.json`, `/openapi.yaml`, `/swagger.json`, `/swagger.yaml`
- `/api-docs`, `/docs`, `/api/docs`
- `/graphql` (with introspection query)
2. Try `OPTIONS` on the base URL and common resource paths
3. Probe common REST patterns: `/api/v1/`, `/api/v2/`, `/v1/`, `/v2/`
4. For each discovered resource, try standard CRUD: `GET /resources`, `GET /resources/:id`, `POST /resources`, etc.
5. Parse response shapes to understand data models
6. Check response headers for rate limit info (`X-RateLimit-*`, `Retry-After`)
7. Check for pagination patterns in responses (`next`, `cursor`, `page`, `offset`)
See `references/discovery-strategies.md` for detailed probing patterns.
### Path C: peek-api Capture
1. Read the capture directory: `endpoints.json`, `auth.json`, `CAPTURE.md`
2. Parse endpoints into the standard catalog format
3. Extract auth headers and cookies from `auth.json`
If peek-api is not installed or no capture exists, tell the user:
```
To capture endpoints from a live site, install peek-api:
git clone https://github.com/alexknowshtml/peek-api
cd peek-api && npm install
node bin/cli.js https://example.com
```
## Step 3: Build Endpoint Catalog
Normalize all discovered endpoints into this format:
```typescript
interface EndpointCatalog {
service: string; // e.g., "stripe", "nexudus"
baseUrl: string;
auth: {
type: 'api-key' | 'bearer' | 'cookies' | 'oauth' | 'none';
headerName?: string; // e.g., "Authorization", "X-API-Key"
envVar: string; // e.g., "STRIPE_API_KEY"
};
pagination?: {
style: 'cursor' | 'offset' | 'page' | 'link-header';
paramName: string; // e.g., "starting_after", "offset", "page"
responseField: string; // e.g., "has_more", "next", "next_page_url"
};
rateLimit?: {
requests: number;
window: string; // e.g., "1m", "1h"
};
resources: ResourceGroup[];
}
interface ResourceGroup {
name: string; // e.g., "customers", "invoices"
description: string;
endpoints: Endpoint[];
}
interface Endpoint {
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
path: string; // e.g., "/v1/customers/:id"
description: string;
parameters: Parameter[];
requestBody?: object; // JSON schema or example
responseExample?: object;
}
interface Parameter {
name: string;
in: 'path' | 'query' | 'header';
required: boolean;
type: string;
description: string;
}
```
Present the catalog to the user for review before generating:
```
Found 24 endpoints across 5 resources:
customers (6 endpoints): list, get, create, update, delete, search
invoices (5 endpoints): list, get, create, send, void
...
Ready to generate the CLI?
```
## Step 4: Generate CLI
Generate a dual-mode CLI using Commander.js. The CLI auto-detects human vs agent output via `process.stdout.isTTY`.
### File Structure
**In-project scaffold:**
```
scripts/
{service}.ts # Entry point with shebang
{service}/
lib/
client.ts # API client (auth, pagination, retry, caching)
envelope.ts # Agent JSON envelope helpers
commands/
{resource}.ts # One file per resource group
```
**Standalone project:**
```
{service}-cli/
package.json
tsconfig.json
bin/
{service}.ts # Entry point with shebang
src/
lib/
client.ts
envelope.ts
commands/
{resource}.ts
```
### Code Generation Patterns
See these references for the patterns to apply during generation:
- `references/api-client-template.md` -- API client class with pagination, retry, rate limiting, caching
- `references/agent-first-patterns.md` -- JSON envelope, HATEOAS next_actions, context-safe output, error fix suggestions
- `references/commander-patterns.md` -- Commander.js subcommands, global options, interactive prompts, colored output
### Key Generation Rules
**Entry point (`{service}.ts`):**
- Shebang: `#!/usr/bin/env npx tsx`
- Self-documenting root command (no args → prints full command tree as JSON)
- Global options: `--json` (force JSON output), `--verbose`, `--config <path>`
**API client (`lib/client.ts`):**
- Constructor takes base URL + auth config
- Auth from env var (name based on `catalog.auth.envVar`)
- Built-in pagination matching the API's pattern
- Retry with exponential backoff for 5xx and 429 errors
- Rate limiting based on discovered limits
- Optional response caching
**Envelope helpers (`lib/envelope.ts`):**
```typescript
const isAgent = !process.stdout.isTTY;
function respond(command: string, result: any, nextActions: Action[] = []) {
if (isAgent) {
console.log(JSON.stringify({ ok: true, command, result, next_actions: nextActions }));
} else {
return result; // caller handles human rendering
}
}
function respondError(command: string, message: string, code: string, fix: string, nextActions: Action[] = []) {
if (isAgent) {
console.log(JSON.stringify({ ok: false, command, error: { message, code }, fix, next_actions: nextActions }));
} else {
console.error(`Error: ${message}`);
console.error(`Fix: ${fix}`);
}
process.exit(1);
}
```
**Command files (`commands/{resource}.ts`):**
- One file per resource group
- Each endpoint becomes a subcommand: `mycli customers list`, `mycli customers get <id>`
- `list` commands: support `--limit`, `--offset`/`--cursor`, `--status` (if filterable)
- `get` commands: take ID as argument
- `create`/`update` commands: accept `--data <json>` or individual `--field` flags
- Every command includes contextual `next_actions` for agent mode
- Errors include `fix` suggestions
**Standalone project extras:**
- `package.json` with `commander`, `tsx` as dependencies, `bin` field pointing to entry
- `tsconfig.json` for TypeScript
- `.env.example` with the required env var
## Step 5: Verify
After generating the CLI:
1. **Verify it runs:** Execute with no args, confirm the self-documenting root works
2. **Test one endpoint:** Pick a simple GET endpoint, run it, verify output
3. **Move on to Step 6** to wrap the CLI in a skill
## Step 6: Generate Skill
Create a Claude Code skill folder that teaches Claude how to use the generated CLI. This is the final step -- it turns the CLI into something any Claude session can pick up and use without reading the code.
### Skill Structure
```
.claude/skills/{service}/
SKILL.md # Skill instructions
```
### SKILL.md Template
Generate a SKILL.md with this structure:
```markdown
---
name: {service}
description: Interact with the {Service} API via CLI. Use when user wants to
{list of actions based on discovered resources, e.g., "list customers,
create invoices, check order status"}. Commands: {service} {resource} {action}.
---
# {Service} CLI
CLI wrapper for the {Service} API.
## Setup
Set the `{SERVICE_ENV_VAR}` environment variable:
\`\`\`bash
export {SERVICE_ENV_VAR}=your-api-key-here
\`\`\`
## Commands
{For each resource group, list commands with examples:}
### {Resource}
\`\`\`bash
# List {resources}
npx tsx {path/to/cli}.ts {resource} list
# Get a specific {resource}
npx tsx {path/to/cli}.ts {resource} get <id>
# Create a {resource}
npx tsx {path/to/cli}.ts {resource} create --field value
\`\`\`
## Common Workflows
{Generate 2-3 practical workflows combining multiple commands:}
### Example: {Workflow name}
\`\`\`bash
# Step 1: Find the customer
npx tsx {path/to/cli}.ts customers list --status=active
# Step 2: Get their invoices
npx tsx {path/to/cli}.ts invoices list --customer-id=abc123
\`\`\`
## Agent Usage
When piped, all commands return JSON with `next_actions`:
\`\`\`bash
npx tsx {path/to/cli}.ts {resource} list | cat
\`\`\`
```
### Key Rules for Skill Generation
1. **Description is critical** -- include specific trigger phrases and list the actions the CLI supports. This is what Claude reads to decide when to use the skill.
2. **Include real command examples** -- use the actual CLI path and real subcommand names from the generated CLI.
3. **Generate practical workflows** -- combine multiple commands into realistic multi-step scenarios based on how the API's resources relate to each other.
4. **Keep it lean** -- the skill should be a quick reference, not a restatement of `--help`. Focus on what Claude needs to know that it can't infer.
### Tell the User
After generating both the CLI and the skill:
```
CLI generated at {cli_path}
Skill generated at .claude/skills/{service}/SKILL.md
To use the CLI directly:
npx tsx {cli_path} # See all commands
npx tsx {cli_path} customers list # List customers
Claude will now automatically use this skill when you ask about {service}.
```
## Reference Files
- `references/discovery-strategies.md` -- Detailed probing patterns, well-known paths, GraphQL introspection, response parsing
- `references/api-client-template.md` -- Full API client class with pagination, retry, rate limiting, caching
- `references/agent-first-patterns.md` -- Agent JSON envelope, HATEOAS, context-safe output, error handling
- `references/commander-patterns.md` -- Commander.js subcommands, nested commands, interactive prompts, colored output, config files, testing
No comments yet. Be the first to comment!