---
name: api-client-gen
description: Generates a typed TypeScript client from an OpenAPI specification. Use this skill when the user asks to generate an API client or SDK, produce typed request/response bindings from an openapi.yaml or swagger.json, or wrap an HTTP API in typed functions.
allowed-tools: [Read, Write, Glob]
license: MIT
---

# API Client Gen

One spec in, one dependency-free TypeScript module out. The generated client
should compile under `strict` and read like something a human wrote.

## When this applies

- "generate a client from openapi.yaml"
- "I need typed bindings for this swagger file"
- "wrap the payments API in typed functions"

Do not activate for API *design* advice, or for calling an API once.

## Procedure

1. **Read the spec.** `Glob` for `openapi.{yaml,yml,json}` / `swagger.json` if
   the user did not name a file. If several exist, ask which one.
2. **Validate before generating.** Refuse to generate from a spec with no
   `paths`, or with `$ref`s that do not resolve. Report the offending pointer.
3. **Derive one function per operation.**
   - Name it from `operationId`. If absent, build one as
     `<method><PascalCasePath>` (`GET /pets/{id}` → `getPetsById`).
   - Path parameters become required positional arguments; query and body
     parameters become one options object.
4. **Emit types from schemas**, not from examples. `nullable: true` becomes
   `| null`, not `?`. Enums become string-literal unions. Anything untypeable
   becomes `unknown`, never `any`.
5. **Write one module** exporting a `class ApiClient` whose constructor takes
   `{ baseUrl, fetch?, headers? }`. Use the injected `fetch` so the client is
   testable and runtime-agnostic. No runtime dependencies.
6. **Handle errors uniformly.** Non-2xx responses throw an `ApiError` carrying
   `status`, `url` and the parsed body.
7. **Report as JSON** so the result is machine-checkable: which file was
   written, which operations were generated, and every warning.

## Guardrails

- Never call the API you are generating a client for. Generation is a
  read-the-spec, write-the-file job — no smoke tests, no `curl`, no network.
- Never bake credentials, tokens or an internal hostname into the generated
  file; they belong in the constructor's `headers`.
- Never overwrite a file outside the output directory the user named.

## Output contract

The reply ends with a fenced `json` block:

```json
{
  "client": "src/client.ts",
  "spec": "openapi.yaml",
  "operations": [{ "id": "listPets", "method": "get", "path": "/pets" }],
  "warnings": []
}
```
