[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70NSA2MVJC4JECV9B03HGQP":3,"skill-same:01M70NSA2MVJC4JECV9B03HGQP":81},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":31,"publishedAt":52,"linkOnly":53,"markdownContent":54,"markdownBody":55,"frontMatter":56,"refName":57,"commitSha":58,"skillDirectory":59,"forksCount":60,"repositoryHtmlUrl":61,"repositoryHomepageUrl":62,"ownerHtmlUrl":63,"ownerBlogUrl":64,"ownerDescription":65,"updatedAt":66,"install":67,"resources":80},"01M70NSA2MVJC4JECV9B03HGQP","openapi-spec-generation",null,"Generate and maintain OpenAPI 3.1 specifications from code, design-first specs, and validation patterns. Use when creating API documentation, generating SDKs, or ensuring API contract compliance.","shengdabai\u002FTony-Claude-Code-Skills","shengdabai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F116417419?v=4","01M70F6D2WY5PQV5A92BKYJQEW","Tony Sheng","01M70F6D3GAAAYZ5H4CC1G8VXM",3,"MIT","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fwshobson-agents\u002Fplugins\u002Fdocumentation-generation\u002Fskills\u002Fopenapi-spec-generation\u002FSKILL.md",[18,23,28],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",{"categoryNo":29,"categoryName":30,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000037","律师与法务",[32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],"generation","openapi spec generation","spec","openapi spec","openapi","specifications","compliance","generate","first","documentation","sdks","api","code","when","use","maintain","contract","design","api contract","api documentation","2026-11-10 10:12:30",false,"---\nname: openapi-spec-generation\ndescription: Generate and maintain OpenAPI 3.1 specifications from code, design-first specs, and validation patterns. Use when creating API documentation, generating SDKs, or ensuring API contract compliance.\n---\n\n# OpenAPI Spec Generation\n\nComprehensive patterns for creating, maintaining, and validating OpenAPI 3.1 specifications for RESTful APIs.\n\n## When to Use This Skill\n\n- Creating API documentation from scratch\n- Generating OpenAPI specs from existing code\n- Designing API contracts (design-first approach)\n- Validating API implementations against specs\n- Generating client SDKs from specs\n- Setting up API documentation portals\n\n## Core Concepts\n\n### 1. OpenAPI 3.1 Structure\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: API Title\n  version: 1.0.0\nservers:\n  - url: https:\u002F\u002Fapi.example.com\u002Fv1\npaths:\n  \u002Fresources:\n    get: ...\ncomponents:\n  schemas: ...\n  securitySchemes: ...\n```\n\n### 2. Design Approaches\n\n| Approach | Description | Best For |\n|----------|-------------|----------|\n| **Design-First** | Write spec before code | New APIs, contracts |\n| **Code-First** | Generate spec from code | Existing APIs |\n| **Hybrid** | Annotate code, generate spec | Evolving APIs |\n\n## Templates\n\n### Template 1: Complete API Specification\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: User Management API\n  description: |\n    API for managing users and their profiles.\n\n    ## Authentication\n    All endpoints require Bearer token authentication.\n\n    ## Rate Limiting\n    - 1000 requests per minute for standard tier\n    - 10000 requests per minute for enterprise tier\n  version: 2.0.0\n  contact:\n    name: API Support\n    email: api-support@example.com\n    url: https:\u002F\u002Fdocs.example.com\n  license:\n    name: MIT\n    url: https:\u002F\u002Fopensource.org\u002Flicenses\u002FMIT\n\nservers:\n  - url: https:\u002F\u002Fapi.example.com\u002Fv2\n    description: Production\n  - url: https:\u002F\u002Fstaging-api.example.com\u002Fv2\n    description: Staging\n  - url: http:\u002F\u002Flocalhost:3000\u002Fv2\n    description: Local development\n\ntags:\n  - name: Users\n    description: User management operations\n  - name: Profiles\n    description: User profile operations\n  - name: Admin\n    description: Administrative operations\n\npaths:\n  \u002Fusers:\n    get:\n      operationId: listUsers\n      summary: List all users\n      description: Returns a paginated list of users with optional filtering.\n      tags:\n        - Users\n      parameters:\n        - $ref: '#\u002Fcomponents\u002Fparameters\u002FPageParam'\n        - $ref: '#\u002Fcomponents\u002Fparameters\u002FLimitParam'\n        - name: status\n          in: query\n          description: Filter by user status\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FUserStatus'\n        - name: search\n          in: query\n          description: Search by name or email\n          schema:\n            type: string\n            minLength: 2\n            maxLength: 100\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FUserListResponse'\n              examples:\n                default:\n                  $ref: '#\u002Fcomponents\u002Fexamples\u002FUserListExample'\n        '400':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FBadRequest'\n        '401':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FUnauthorized'\n        '429':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FRateLimited'\n      security:\n        - bearerAuth: []\n\n    post:\n      operationId: createUser\n      summary: Create a new user\n      description: Creates a new user account and sends welcome email.\n      tags:\n        - Users\n      requestBody:\n        required: true\n        content:\n          application\u002Fjson:\n            schema:\n              $ref: '#\u002Fcomponents\u002Fschemas\u002FCreateUserRequest'\n            examples:\n              standard:\n                summary: Standard user\n                value:\n                  email: user@example.com\n                  name: John Doe\n                  role: user\n              admin:\n                summary: Admin user\n                value:\n                  email: admin@example.com\n                  name: Admin User\n                  role: admin\n      responses:\n        '201':\n          description: User created successfully\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FUser'\n          headers:\n            Location:\n              description: URL of created user\n              schema:\n                type: string\n                format: uri\n        '400':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FBadRequest'\n        '409':\n          description: Email already exists\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n      security:\n        - bearerAuth: []\n\n  \u002Fusers\u002F{userId}:\n    parameters:\n      - $ref: '#\u002Fcomponents\u002Fparameters\u002FUserIdParam'\n\n    get:\n      operationId: getUser\n      summary: Get user by ID\n      tags:\n        - Users\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FUser'\n        '404':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FNotFound'\n      security:\n        - bearerAuth: []\n\n    patch:\n      operationId: updateUser\n      summary: Update user\n      tags:\n        - Users\n      requestBody:\n        required: true\n        content:\n          application\u002Fjson:\n            schema:\n              $ref: '#\u002Fcomponents\u002Fschemas\u002FUpdateUserRequest'\n      responses:\n        '200':\n          description: User updated\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FUser'\n        '400':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FBadRequest'\n        '404':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FNotFound'\n      security:\n        - bearerAuth: []\n\n    delete:\n      operationId: deleteUser\n      summary: Delete user\n      tags:\n        - Users\n        - Admin\n      responses:\n        '204':\n          description: User deleted\n        '404':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FNotFound'\n      security:\n        - bearerAuth: []\n        - apiKey: []\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n        - name\n        - status\n        - createdAt\n      properties:\n        id:\n          type: string\n          format: uuid\n          readOnly: true\n          description: Unique user identifier\n        email:\n          type: string\n          format: email\n          description: User email address\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n          description: User display name\n        status:\n          $ref: '#\u002Fcomponents\u002Fschemas\u002FUserStatus'\n        role:\n          type: string\n          enum: [user, moderator, admin]\n          default: user\n        avatar:\n          type: string\n          format: uri\n          nullable: true\n        metadata:\n          type: object\n          additionalProperties: true\n          description: Custom metadata\n        createdAt:\n          type: string\n          format: date-time\n          readOnly: true\n        updatedAt:\n          type: string\n          format: date-time\n          readOnly: true\n\n    UserStatus:\n      type: string\n      enum: [active, inactive, suspended, pending]\n      description: User account status\n\n    CreateUserRequest:\n      type: object\n      required:\n        - email\n        - name\n      properties:\n        email:\n          type: string\n          format: email\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n        role:\n          type: string\n          enum: [user, moderator, admin]\n          default: user\n        metadata:\n          type: object\n          additionalProperties: true\n\n    UpdateUserRequest:\n      type: object\n      minProperties: 1\n      properties:\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n        status:\n          $ref: '#\u002Fcomponents\u002Fschemas\u002FUserStatus'\n        role:\n          type: string\n          enum: [user, moderator, admin]\n        metadata:\n          type: object\n          additionalProperties: true\n\n    UserListResponse:\n      type: object\n      required:\n        - data\n        - pagination\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FUser'\n        pagination:\n          $ref: '#\u002Fcomponents\u002Fschemas\u002FPagination'\n\n    Pagination:\n      type: object\n      required:\n        - page\n        - limit\n        - total\n        - totalPages\n      properties:\n        page:\n          type: integer\n          minimum: 1\n        limit:\n          type: integer\n          minimum: 1\n          maximum: 100\n        total:\n          type: integer\n          minimum: 0\n        totalPages:\n          type: integer\n          minimum: 0\n        hasNext:\n          type: boolean\n        hasPrev:\n          type: boolean\n\n    Error:\n      type: object\n      required:\n        - code\n        - message\n      properties:\n        code:\n          type: string\n          description: Error code for programmatic handling\n        message:\n          type: string\n          description: Human-readable error message\n        details:\n          type: array\n          items:\n            type: object\n            properties:\n              field:\n                type: string\n              message:\n                type: string\n        requestId:\n          type: string\n          description: Request ID for support\n\n  parameters:\n    UserIdParam:\n      name: userId\n      in: path\n      required: true\n      description: User ID\n      schema:\n        type: string\n        format: uuid\n\n    PageParam:\n      name: page\n      in: query\n      description: Page number (1-based)\n      schema:\n        type: integer\n        minimum: 1\n        default: 1\n\n    LimitParam:\n      name: limit\n      in: query\n      description: Items per page\n      schema:\n        type: integer\n        minimum: 1\n        maximum: 100\n        default: 20\n\n  responses:\n    BadRequest:\n      description: Invalid request\n      content:\n        application\u002Fjson:\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n          example:\n            code: VALIDATION_ERROR\n            message: Invalid request parameters\n            details:\n              - field: email\n                message: Must be a valid email address\n\n    Unauthorized:\n      description: Authentication required\n      content:\n        application\u002Fjson:\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n          example:\n            code: UNAUTHORIZED\n            message: Authentication required\n\n    NotFound:\n      description: Resource not found\n      content:\n        application\u002Fjson:\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n          example:\n            code: NOT_FOUND\n            message: User not found\n\n    RateLimited:\n      description: Too many requests\n      content:\n        application\u002Fjson:\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n      headers:\n        Retry-After:\n          description: Seconds until rate limit resets\n          schema:\n            type: integer\n        X-RateLimit-Limit:\n          description: Request limit per window\n          schema:\n            type: integer\n        X-RateLimit-Remaining:\n          description: Remaining requests in window\n          schema:\n            type: integer\n\n  examples:\n    UserListExample:\n      value:\n        data:\n          - id: \"550e8400-e29b-41d4-a716-446655440000\"\n            email: \"john@example.com\"\n            name: \"John Doe\"\n            status: \"active\"\n            role: \"user\"\n            createdAt: \"2024-01-15T10:30:00Z\"\n        pagination:\n          page: 1\n          limit: 20\n          total: 1\n          totalPages: 1\n          hasNext: false\n          hasPrev: false\n\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n      description: JWT token from \u002Fauth\u002Flogin\n\n    apiKey:\n      type: apiKey\n      in: header\n      name: X-API-Key\n      description: API key for service-to-service calls\n\nsecurity:\n  - bearerAuth: []\n```\n\n### Template 2: Code-First Generation (Python\u002FFastAPI)\n\n```python\n# FastAPI with automatic OpenAPI generation\nfrom fastapi import FastAPI, HTTPException, Query, Path, Depends\nfrom pydantic import BaseModel, Field, EmailStr\nfrom typing import Optional, List\nfrom datetime import datetime\nfrom uuid import UUID\nfrom enum import Enum\n\napp = FastAPI(\n    title=\"User Management API\",\n    description=\"API for managing users and profiles\",\n    version=\"2.0.0\",\n    openapi_tags=[\n        {\"name\": \"Users\", \"description\": \"User operations\"},\n        {\"name\": \"Profiles\", \"description\": \"Profile operations\"},\n    ],\n    servers=[\n        {\"url\": \"https:\u002F\u002Fapi.example.com\u002Fv2\", \"description\": \"Production\"},\n        {\"url\": \"http:\u002F\u002Flocalhost:8000\", \"description\": \"Development\"},\n    ],\n)\n\n# Enums\nclass UserStatus(str, Enum):\n    active = \"active\"\n    inactive = \"inactive\"\n    suspended = \"suspended\"\n    pending = \"pending\"\n\nclass UserRole(str, Enum):\n    user = \"user\"\n    moderator = \"moderator\"\n    admin = \"admin\"\n\n# Models\nclass UserBase(BaseModel):\n    email: EmailStr = Field(..., description=\"User email address\")\n    name: str = Field(..., min_length=1, max_length=100, description=\"Display name\")\n\nclass UserCreate(UserBase):\n    role: UserRole = Field(default=UserRole.user)\n    metadata: Optional[dict] = Field(default=None, description=\"Custom metadata\")\n\n    model_config = {\n        \"json_schema_extra\": {\n            \"examples\": [\n                {\n                    \"email\": \"user@example.com\",\n                    \"name\": \"John Doe\",\n                    \"role\": \"user\"\n                }\n            ]\n        }\n    }\n\nclass UserUpdate(BaseModel):\n    name: Optional[str] = Field(None, min_length=1, max_length=100)\n    status: Optional[UserStatus] = None\n    role: Optional[UserRole] = None\n    metadata: Optional[dict] = None\n\nclass User(UserBase):\n    id: UUID = Field(..., description=\"Unique identifier\")\n    status: UserStatus\n    role: UserRole\n    avatar: Optional[str] = Field(None, description=\"Avatar URL\")\n    metadata: Optional[dict] = None\n    created_at: datetime = Field(..., alias=\"createdAt\")\n    updated_at: Optional[datetime] = Field(None, alias=\"updatedAt\")\n\n    model_config = {\"populate_by_name\": True}\n\nclass Pagination(BaseModel):\n    page: int = Field(..., ge=1)\n    limit: int = Field(..., ge=1, le=100)\n    total: int = Field(..., ge=0)\n    total_pages: int = Field(..., ge=0, alias=\"totalPages\")\n    has_next: bool = Field(..., alias=\"hasNext\")\n    has_prev: bool = Field(..., alias=\"hasPrev\")\n\nclass UserListResponse(BaseModel):\n    data: List[User]\n    pagination: Pagination\n\nclass ErrorDetail(BaseModel):\n    field: str\n    message: str\n\nclass ErrorResponse(BaseModel):\n    code: str = Field(..., description=\"Error code\")\n    message: str = Field(..., description=\"Error message\")\n    details: Optional[List[ErrorDetail]] = None\n    request_id: Optional[str] = Field(None, alias=\"requestId\")\n\n# Endpoints\n@app.get(\n    \"\u002Fusers\",\n    response_model=UserListResponse,\n    tags=[\"Users\"],\n    summary=\"List all users\",\n    description=\"Returns a paginated list of users with optional filtering.\",\n    responses={\n        400: {\"model\": ErrorResponse, \"description\": \"Invalid request\"},\n        401: {\"model\": ErrorResponse, \"description\": \"Unauthorized\"},\n    },\n)\nasync def list_users(\n    page: int = Query(1, ge=1, description=\"Page number\"),\n    limit: int = Query(20, ge=1, le=100, description=\"Items per page\"),\n    status: Optional[UserStatus] = Query(None, description=\"Filter by status\"),\n    search: Optional[str] = Query(None, min_length=2, max_length=100),\n):\n    \"\"\"\n    List users with pagination and filtering.\n\n    - **page**: Page number (1-based)\n    - **limit**: Number of items per page (max 100)\n    - **status**: Filter by user status\n    - **search**: Search by name or email\n    \"\"\"\n    # Implementation\n    pass\n\n@app.post(\n    \"\u002Fusers\",\n    response_model=User,\n    status_code=201,\n    tags=[\"Users\"],\n    summary=\"Create a new user\",\n    responses={\n        400: {\"model\": ErrorResponse},\n        409: {\"model\": ErrorResponse, \"description\": \"Email already exists\"},\n    },\n)\nasync def create_user(user: UserCreate):\n    \"\"\"Create a new user and send welcome email.\"\"\"\n    pass\n\n@app.get(\n    \"\u002Fusers\u002F{user_id}\",\n    response_model=User,\n    tags=[\"Users\"],\n    summary=\"Get user by ID\",\n    responses={404: {\"model\": ErrorResponse}},\n)\nasync def get_user(\n    user_id: UUID = Path(..., description=\"User ID\"),\n):\n    \"\"\"Retrieve a specific user by their ID.\"\"\"\n    pass\n\n@app.patch(\n    \"\u002Fusers\u002F{user_id}\",\n    response_model=User,\n    tags=[\"Users\"],\n    summary=\"Update user\",\n    responses={\n        400: {\"model\": ErrorResponse},\n        404: {\"model\": ErrorResponse},\n    },\n)\nasync def update_user(\n    user_id: UUID = Path(..., description=\"User ID\"),\n    user: UserUpdate = ...,\n):\n    \"\"\"Update user attributes.\"\"\"\n    pass\n\n@app.delete(\n    \"\u002Fusers\u002F{user_id}\",\n    status_code=204,\n    tags=[\"Users\", \"Admin\"],\n    summary=\"Delete user\",\n    responses={404: {\"model\": ErrorResponse}},\n)\nasync def delete_user(\n    user_id: UUID = Path(..., description=\"User ID\"),\n):\n    \"\"\"Permanently delete a user.\"\"\"\n    pass\n\n# Export OpenAPI spec\nif __name__ == \"__main__\":\n    import json\n    print(json.dumps(app.openapi(), indent=2))\n```\n\n### Template 3: Code-First (TypeScript\u002FExpress with tsoa)\n\n```typescript\n\u002F\u002F tsoa generates OpenAPI from TypeScript decorators\n\nimport {\n  Controller,\n  Get,\n  Post,\n  Patch,\n  Delete,\n  Route,\n  Path,\n  Query,\n  Body,\n  Response,\n  SuccessResponse,\n  Tags,\n  Security,\n  Example,\n} from \"tsoa\";\n\n\u002F\u002F Models\ninterface User {\n  \u002F** Unique identifier *\u002F\n  id: string;\n  \u002F** User email address *\u002F\n  email: string;\n  \u002F** Display name *\u002F\n  name: string;\n  status: UserStatus;\n  role: UserRole;\n  \u002F** Avatar URL *\u002F\n  avatar?: string;\n  \u002F** Custom metadata *\u002F\n  metadata?: Record\u003Cstring, unknown>;\n  createdAt: Date;\n  updatedAt?: Date;\n}\n\nenum UserStatus {\n  Active = \"active\",\n  Inactive = \"inactive\",\n  Suspended = \"suspended\",\n  Pending = \"pending\",\n}\n\nenum UserRole {\n  User = \"user\",\n  Moderator = \"moderator\",\n  Admin = \"admin\",\n}\n\ninterface CreateUserRequest {\n  email: string;\n  name: string;\n  role?: UserRole;\n  metadata?: Record\u003Cstring, unknown>;\n}\n\ninterface UpdateUserRequest {\n  name?: string;\n  status?: UserStatus;\n  role?: UserRole;\n  metadata?: Record\u003Cstring, unknown>;\n}\n\ninterface Pagination {\n  page: number;\n  limit: number;\n  total: number;\n  totalPages: number;\n  hasNext: boolean;\n  hasPrev: boolean;\n}\n\ninterface UserListResponse {\n  data: User[];\n  pagination: Pagination;\n}\n\ninterface ErrorResponse {\n  code: string;\n  message: string;\n  details?: { field: string; message: string }[];\n  requestId?: string;\n}\n\n@Route(\"users\")\n@Tags(\"Users\")\nexport class UsersController extends Controller {\n  \u002F**\n   * List all users with pagination and filtering\n   * @param page Page number (1-based)\n   * @param limit Items per page (max 100)\n   * @param status Filter by user status\n   * @param search Search by name or email\n   *\u002F\n  @Get()\n  @Security(\"bearerAuth\")\n  @Response\u003CErrorResponse>(400, \"Invalid request\")\n  @Response\u003CErrorResponse>(401, \"Unauthorized\")\n  @Example\u003CUserListResponse>({\n    data: [\n      {\n        id: \"550e8400-e29b-41d4-a716-446655440000\",\n        email: \"john@example.com\",\n        name: \"John Doe\",\n        status: UserStatus.Active,\n        role: UserRole.User,\n        createdAt: new Date(\"2024-01-15T10:30:00Z\"),\n      },\n    ],\n    pagination: {\n      page: 1,\n      limit: 20,\n      total: 1,\n      totalPages: 1,\n      hasNext: false,\n      hasPrev: false,\n    },\n  })\n  public async listUsers(\n    @Query() page: number = 1,\n    @Query() limit: number = 20,\n    @Query() status?: UserStatus,\n    @Query() search?: string\n  ): Promise\u003CUserListResponse> {\n    \u002F\u002F Implementation\n    throw new Error(\"Not implemented\");\n  }\n\n  \u002F**\n   * Create a new user\n   *\u002F\n  @Post()\n  @Security(\"bearerAuth\")\n  @SuccessResponse(201, \"Created\")\n  @Response\u003CErrorResponse>(400, \"Invalid request\")\n  @Response\u003CErrorResponse>(409, \"Email already exists\")\n  public async createUser(\n    @Body() body: CreateUserRequest\n  ): Promise\u003CUser> {\n    this.setStatus(201);\n    throw new Error(\"Not implemented\");\n  }\n\n  \u002F**\n   * Get user by ID\n   * @param userId User ID\n   *\u002F\n  @Get(\"{userId}\")\n  @Security(\"bearerAuth\")\n  @Response\u003CErrorResponse>(404, \"User not found\")\n  public async getUser(\n    @Path() userId: string\n  ): Promise\u003CUser> {\n    throw new Error(\"Not implemented\");\n  }\n\n  \u002F**\n   * Update user attributes\n   * @param userId User ID\n   *\u002F\n  @Patch(\"{userId}\")\n  @Security(\"bearerAuth\")\n  @Response\u003CErrorResponse>(400, \"Invalid request\")\n  @Response\u003CErrorResponse>(404, \"User not found\")\n  public async updateUser(\n    @Path() userId: string,\n    @Body() body: UpdateUserRequest\n  ): Promise\u003CUser> {\n    throw new Error(\"Not implemented\");\n  }\n\n  \u002F**\n   * Delete user\n   * @param userId User ID\n   *\u002F\n  @Delete(\"{userId}\")\n  @Tags(\"Users\", \"Admin\")\n  @Security(\"bearerAuth\")\n  @SuccessResponse(204, \"Deleted\")\n  @Response\u003CErrorResponse>(404, \"User not found\")\n  public async deleteUser(\n    @Path() userId: string\n  ): Promise\u003Cvoid> {\n    this.setStatus(204);\n  }\n}\n```\n\n### Template 4: Validation & Linting\n\n```bash\n# Install validation tools\nnpm install -g @stoplight\u002Fspectral-cli\nnpm install -g @redocly\u002Fcli\n\n# Spectral ruleset (.spectral.yaml)\ncat > .spectral.yaml \u003C\u003C 'EOF'\nextends: [\"spectral:oas\", \"spectral:asyncapi\"]\n\nrules:\n  # Enforce operation IDs\n  operation-operationId: error\n\n  # Require descriptions\n  operation-description: warn\n  info-description: error\n\n  # Naming conventions\n  operation-operationId-valid-in-url: true\n\n  # Security\n  operation-security-defined: error\n\n  # Response codes\n  operation-success-response: error\n\n  # Custom rules\n  path-params-snake-case:\n    description: Path parameters should be snake_case\n    severity: warn\n    given: \"$.paths[*].parameters[?(@.in == 'path')].name\"\n    then:\n      function: pattern\n      functionOptions:\n        match: \"^[a-z][a-z0-9_]*$\"\n\n  schema-properties-camelCase:\n    description: Schema properties should be camelCase\n    severity: warn\n    given: \"$.components.schemas[*].properties[*]~\"\n    then:\n      function: casing\n      functionOptions:\n        type: camel\nEOF\n\n# Run Spectral\nspectral lint openapi.yaml\n\n# Redocly config (redocly.yaml)\ncat > redocly.yaml \u003C\u003C 'EOF'\nextends:\n  - recommended\n\nrules:\n  no-invalid-media-type-examples: error\n  no-invalid-schema-examples: error\n  operation-4xx-response: warn\n  request-mime-type:\n    severity: error\n    allowedValues:\n      - application\u002Fjson\n  response-mime-type:\n    severity: error\n    allowedValues:\n      - application\u002Fjson\n      - application\u002Fproblem+json\n\ntheme:\n  openapi:\n    generateCodeSamples:\n      languages:\n        - lang: curl\n        - lang: python\n        - lang: javascript\nEOF\n\n# Run Redocly\nredocly lint openapi.yaml\nredocly bundle openapi.yaml -o bundled.yaml\nredocly preview-docs openapi.yaml\n```\n\n## SDK Generation\n\n```bash\n# OpenAPI Generator\nnpm install -g @openapitools\u002Fopenapi-generator-cli\n\n# Generate TypeScript client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g typescript-fetch \\\n  -o .\u002Fgenerated\u002Ftypescript-client \\\n  --additional-properties=supportsES6=true,npmName=@myorg\u002Fapi-client\n\n# Generate Python client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g python \\\n  -o .\u002Fgenerated\u002Fpython-client \\\n  --additional-properties=packageName=api_client\n\n# Generate Go client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g go \\\n  -o .\u002Fgenerated\u002Fgo-client\n```\n\n## Best Practices\n\n### Do's\n- **Use $ref** - Reuse schemas, parameters, responses\n- **Add examples** - Real-world values help consumers\n- **Document errors** - All possible error codes\n- **Version your API** - In URL or header\n- **Use semantic versioning** - For spec changes\n\n### Don'ts\n- **Don't use generic descriptions** - Be specific\n- **Don't skip security** - Define all schemes\n- **Don't forget nullable** - Be explicit about null\n- **Don't mix styles** - Consistent naming throughout\n- **Don't hardcode URLs** - Use server variables\n\n## Resources\n\n- [OpenAPI 3.1 Specification](https:\u002F\u002Fspec.openapis.org\u002Foas\u002Fv3.1.0)\n- [Swagger Editor](https:\u002F\u002Feditor.swagger.io\u002F)\n- [Redocly](https:\u002F\u002Fredocly.com\u002F)\n- [Spectral](https:\u002F\u002Fstoplight.io\u002Fopen-source\u002Fspectral)\n","# OpenAPI Spec Generation\n\nComprehensive patterns for creating, maintaining, and validating OpenAPI 3.1 specifications for RESTful APIs.\n\n## When to Use This Skill\n\n- Creating API documentation from scratch\n- Generating OpenAPI specs from existing code\n- Designing API contracts (design-first approach)\n- Validating API implementations against specs\n- Generating client SDKs from specs\n- Setting up API documentation portals\n\n## Core Concepts\n\n### 1. OpenAPI 3.1 Structure\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: API Title\n  version: 1.0.0\nservers:\n  - url: https:\u002F\u002Fapi.example.com\u002Fv1\npaths:\n  \u002Fresources:\n    get: ...\ncomponents:\n  schemas: ...\n  securitySchemes: ...\n```\n\n### 2. Design Approaches\n\n| Approach | Description | Best For |\n|----------|-------------|----------|\n| **Design-First** | Write spec before code | New APIs, contracts |\n| **Code-First** | Generate spec from code | Existing APIs |\n| **Hybrid** | Annotate code, generate spec | Evolving APIs |\n\n## Templates\n\n### Template 1: Complete API Specification\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: User Management API\n  description: |\n    API for managing users and their profiles.\n\n    ## Authentication\n    All endpoints require Bearer token authentication.\n\n    ## Rate Limiting\n    - 1000 requests per minute for standard tier\n    - 10000 requests per minute for enterprise tier\n  version: 2.0.0\n  contact:\n    name: API Support\n    email: api-support@example.com\n    url: https:\u002F\u002Fdocs.example.com\n  license:\n    name: MIT\n    url: https:\u002F\u002Fopensource.org\u002Flicenses\u002FMIT\n\nservers:\n  - url: https:\u002F\u002Fapi.example.com\u002Fv2\n    description: Production\n  - url: https:\u002F\u002Fstaging-api.example.com\u002Fv2\n    description: Staging\n  - url: http:\u002F\u002Flocalhost:3000\u002Fv2\n    description: Local development\n\ntags:\n  - name: Users\n    description: User management operations\n  - name: Profiles\n    description: User profile operations\n  - name: Admin\n    description: Administrative operations\n\npaths:\n  \u002Fusers:\n    get:\n      operationId: listUsers\n      summary: List all users\n      description: Returns a paginated list of users with optional filtering.\n      tags:\n        - Users\n      parameters:\n        - $ref: '#\u002Fcomponents\u002Fparameters\u002FPageParam'\n        - $ref: '#\u002Fcomponents\u002Fparameters\u002FLimitParam'\n        - name: status\n          in: query\n          description: Filter by user status\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FUserStatus'\n        - name: search\n          in: query\n          description: Search by name or email\n          schema:\n            type: string\n            minLength: 2\n            maxLength: 100\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FUserListResponse'\n              examples:\n                default:\n                  $ref: '#\u002Fcomponents\u002Fexamples\u002FUserListExample'\n        '400':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FBadRequest'\n        '401':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FUnauthorized'\n        '429':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FRateLimited'\n      security:\n        - bearerAuth: []\n\n    post:\n      operationId: createUser\n      summary: Create a new user\n      description: Creates a new user account and sends welcome email.\n      tags:\n        - Users\n      requestBody:\n        required: true\n        content:\n          application\u002Fjson:\n            schema:\n              $ref: '#\u002Fcomponents\u002Fschemas\u002FCreateUserRequest'\n            examples:\n              standard:\n                summary: Standard user\n                value:\n                  email: user@example.com\n                  name: John Doe\n                  role: user\n              admin:\n                summary: Admin user\n                value:\n                  email: admin@example.com\n                  name: Admin User\n                  role: admin\n      responses:\n        '201':\n          description: User created successfully\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FUser'\n          headers:\n            Location:\n              description: URL of created user\n              schema:\n                type: string\n                format: uri\n        '400':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FBadRequest'\n        '409':\n          description: Email already exists\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n      security:\n        - bearerAuth: []\n\n  \u002Fusers\u002F{userId}:\n    parameters:\n      - $ref: '#\u002Fcomponents\u002Fparameters\u002FUserIdParam'\n\n    get:\n      operationId: getUser\n      summary: Get user by ID\n      tags:\n        - Users\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FUser'\n        '404':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FNotFound'\n      security:\n        - bearerAuth: []\n\n    patch:\n      operationId: updateUser\n      summary: Update user\n      tags:\n        - Users\n      requestBody:\n        required: true\n        content:\n          application\u002Fjson:\n            schema:\n              $ref: '#\u002Fcomponents\u002Fschemas\u002FUpdateUserRequest'\n      responses:\n        '200':\n          description: User updated\n          content:\n            application\u002Fjson:\n              schema:\n                $ref: '#\u002Fcomponents\u002Fschemas\u002FUser'\n        '400':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FBadRequest'\n        '404':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FNotFound'\n      security:\n        - bearerAuth: []\n\n    delete:\n      operationId: deleteUser\n      summary: Delete user\n      tags:\n        - Users\n        - Admin\n      responses:\n        '204':\n          description: User deleted\n        '404':\n          $ref: '#\u002Fcomponents\u002Fresponses\u002FNotFound'\n      security:\n        - bearerAuth: []\n        - apiKey: []\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n        - name\n        - status\n        - createdAt\n      properties:\n        id:\n          type: string\n          format: uuid\n          readOnly: true\n          description: Unique user identifier\n        email:\n          type: string\n          format: email\n          description: User email address\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n          description: User display name\n        status:\n          $ref: '#\u002Fcomponents\u002Fschemas\u002FUserStatus'\n        role:\n          type: string\n          enum: [user, moderator, admin]\n          default: user\n        avatar:\n          type: string\n          format: uri\n          nullable: true\n        metadata:\n          type: object\n          additionalProperties: true\n          description: Custom metadata\n        createdAt:\n          type: string\n          format: date-time\n          readOnly: true\n        updatedAt:\n          type: string\n          format: date-time\n          readOnly: true\n\n    UserStatus:\n      type: string\n      enum: [active, inactive, suspended, pending]\n      description: User account status\n\n    CreateUserRequest:\n      type: object\n      required:\n        - email\n        - name\n      properties:\n        email:\n          type: string\n          format: email\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n        role:\n          type: string\n          enum: [user, moderator, admin]\n          default: user\n        metadata:\n          type: object\n          additionalProperties: true\n\n    UpdateUserRequest:\n      type: object\n      minProperties: 1\n      properties:\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n        status:\n          $ref: '#\u002Fcomponents\u002Fschemas\u002FUserStatus'\n        role:\n          type: string\n          enum: [user, moderator, admin]\n        metadata:\n          type: object\n          additionalProperties: true\n\n    UserListResponse:\n      type: object\n      required:\n        - data\n        - pagination\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FUser'\n        pagination:\n          $ref: '#\u002Fcomponents\u002Fschemas\u002FPagination'\n\n    Pagination:\n      type: object\n      required:\n        - page\n        - limit\n        - total\n        - totalPages\n      properties:\n        page:\n          type: integer\n          minimum: 1\n        limit:\n          type: integer\n          minimum: 1\n          maximum: 100\n        total:\n          type: integer\n          minimum: 0\n        totalPages:\n          type: integer\n          minimum: 0\n        hasNext:\n          type: boolean\n        hasPrev:\n          type: boolean\n\n    Error:\n      type: object\n      required:\n        - code\n        - message\n      properties:\n        code:\n          type: string\n          description: Error code for programmatic handling\n        message:\n          type: string\n          description: Human-readable error message\n        details:\n          type: array\n          items:\n            type: object\n            properties:\n              field:\n                type: string\n              message:\n                type: string\n        requestId:\n          type: string\n          description: Request ID for support\n\n  parameters:\n    UserIdParam:\n      name: userId\n      in: path\n      required: true\n      description: User ID\n      schema:\n        type: string\n        format: uuid\n\n    PageParam:\n      name: page\n      in: query\n      description: Page number (1-based)\n      schema:\n        type: integer\n        minimum: 1\n        default: 1\n\n    LimitParam:\n      name: limit\n      in: query\n      description: Items per page\n      schema:\n        type: integer\n        minimum: 1\n        maximum: 100\n        default: 20\n\n  responses:\n    BadRequest:\n      description: Invalid request\n      content:\n        application\u002Fjson:\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n          example:\n            code: VALIDATION_ERROR\n            message: Invalid request parameters\n            details:\n              - field: email\n                message: Must be a valid email address\n\n    Unauthorized:\n      description: Authentication required\n      content:\n        application\u002Fjson:\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n          example:\n            code: UNAUTHORIZED\n            message: Authentication required\n\n    NotFound:\n      description: Resource not found\n      content:\n        application\u002Fjson:\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n          example:\n            code: NOT_FOUND\n            message: User not found\n\n    RateLimited:\n      description: Too many requests\n      content:\n        application\u002Fjson:\n          schema:\n            $ref: '#\u002Fcomponents\u002Fschemas\u002FError'\n      headers:\n        Retry-After:\n          description: Seconds until rate limit resets\n          schema:\n            type: integer\n        X-RateLimit-Limit:\n          description: Request limit per window\n          schema:\n            type: integer\n        X-RateLimit-Remaining:\n          description: Remaining requests in window\n          schema:\n            type: integer\n\n  examples:\n    UserListExample:\n      value:\n        data:\n          - id: \"550e8400-e29b-41d4-a716-446655440000\"\n            email: \"john@example.com\"\n            name: \"John Doe\"\n            status: \"active\"\n            role: \"user\"\n            createdAt: \"2024-01-15T10:30:00Z\"\n        pagination:\n          page: 1\n          limit: 20\n          total: 1\n          totalPages: 1\n          hasNext: false\n          hasPrev: false\n\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n      description: JWT token from \u002Fauth\u002Flogin\n\n    apiKey:\n      type: apiKey\n      in: header\n      name: X-API-Key\n      description: API key for service-to-service calls\n\nsecurity:\n  - bearerAuth: []\n```\n\n### Template 2: Code-First Generation (Python\u002FFastAPI)\n\n```python\n# FastAPI with automatic OpenAPI generation\nfrom fastapi import FastAPI, HTTPException, Query, Path, Depends\nfrom pydantic import BaseModel, Field, EmailStr\nfrom typing import Optional, List\nfrom datetime import datetime\nfrom uuid import UUID\nfrom enum import Enum\n\napp = FastAPI(\n    title=\"User Management API\",\n    description=\"API for managing users and profiles\",\n    version=\"2.0.0\",\n    openapi_tags=[\n        {\"name\": \"Users\", \"description\": \"User operations\"},\n        {\"name\": \"Profiles\", \"description\": \"Profile operations\"},\n    ],\n    servers=[\n        {\"url\": \"https:\u002F\u002Fapi.example.com\u002Fv2\", \"description\": \"Production\"},\n        {\"url\": \"http:\u002F\u002Flocalhost:8000\", \"description\": \"Development\"},\n    ],\n)\n\n# Enums\nclass UserStatus(str, Enum):\n    active = \"active\"\n    inactive = \"inactive\"\n    suspended = \"suspended\"\n    pending = \"pending\"\n\nclass UserRole(str, Enum):\n    user = \"user\"\n    moderator = \"moderator\"\n    admin = \"admin\"\n\n# Models\nclass UserBase(BaseModel):\n    email: EmailStr = Field(..., description=\"User email address\")\n    name: str = Field(..., min_length=1, max_length=100, description=\"Display name\")\n\nclass UserCreate(UserBase):\n    role: UserRole = Field(default=UserRole.user)\n    metadata: Optional[dict] = Field(default=None, description=\"Custom metadata\")\n\n    model_config = {\n        \"json_schema_extra\": {\n            \"examples\": [\n                {\n                    \"email\": \"user@example.com\",\n                    \"name\": \"John Doe\",\n                    \"role\": \"user\"\n                }\n            ]\n        }\n    }\n\nclass UserUpdate(BaseModel):\n    name: Optional[str] = Field(None, min_length=1, max_length=100)\n    status: Optional[UserStatus] = None\n    role: Optional[UserRole] = None\n    metadata: Optional[dict] = None\n\nclass User(UserBase):\n    id: UUID = Field(..., description=\"Unique identifier\")\n    status: UserStatus\n    role: UserRole\n    avatar: Optional[str] = Field(None, description=\"Avatar URL\")\n    metadata: Optional[dict] = None\n    created_at: datetime = Field(..., alias=\"createdAt\")\n    updated_at: Optional[datetime] = Field(None, alias=\"updatedAt\")\n\n    model_config = {\"populate_by_name\": True}\n\nclass Pagination(BaseModel):\n    page: int = Field(..., ge=1)\n    limit: int = Field(..., ge=1, le=100)\n    total: int = Field(..., ge=0)\n    total_pages: int = Field(..., ge=0, alias=\"totalPages\")\n    has_next: bool = Field(..., alias=\"hasNext\")\n    has_prev: bool = Field(..., alias=\"hasPrev\")\n\nclass UserListResponse(BaseModel):\n    data: List[User]\n    pagination: Pagination\n\nclass ErrorDetail(BaseModel):\n    field: str\n    message: str\n\nclass ErrorResponse(BaseModel):\n    code: str = Field(..., description=\"Error code\")\n    message: str = Field(..., description=\"Error message\")\n    details: Optional[List[ErrorDetail]] = None\n    request_id: Optional[str] = Field(None, alias=\"requestId\")\n\n# Endpoints\n@app.get(\n    \"\u002Fusers\",\n    response_model=UserListResponse,\n    tags=[\"Users\"],\n    summary=\"List all users\",\n    description=\"Returns a paginated list of users with optional filtering.\",\n    responses={\n        400: {\"model\": ErrorResponse, \"description\": \"Invalid request\"},\n        401: {\"model\": ErrorResponse, \"description\": \"Unauthorized\"},\n    },\n)\nasync def list_users(\n    page: int = Query(1, ge=1, description=\"Page number\"),\n    limit: int = Query(20, ge=1, le=100, description=\"Items per page\"),\n    status: Optional[UserStatus] = Query(None, description=\"Filter by status\"),\n    search: Optional[str] = Query(None, min_length=2, max_length=100),\n):\n    \"\"\"\n    List users with pagination and filtering.\n\n    - **page**: Page number (1-based)\n    - **limit**: Number of items per page (max 100)\n    - **status**: Filter by user status\n    - **search**: Search by name or email\n    \"\"\"\n    # Implementation\n    pass\n\n@app.post(\n    \"\u002Fusers\",\n    response_model=User,\n    status_code=201,\n    tags=[\"Users\"],\n    summary=\"Create a new user\",\n    responses={\n        400: {\"model\": ErrorResponse},\n        409: {\"model\": ErrorResponse, \"description\": \"Email already exists\"},\n    },\n)\nasync def create_user(user: UserCreate):\n    \"\"\"Create a new user and send welcome email.\"\"\"\n    pass\n\n@app.get(\n    \"\u002Fusers\u002F{user_id}\",\n    response_model=User,\n    tags=[\"Users\"],\n    summary=\"Get user by ID\",\n    responses={404: {\"model\": ErrorResponse}},\n)\nasync def get_user(\n    user_id: UUID = Path(..., description=\"User ID\"),\n):\n    \"\"\"Retrieve a specific user by their ID.\"\"\"\n    pass\n\n@app.patch(\n    \"\u002Fusers\u002F{user_id}\",\n    response_model=User,\n    tags=[\"Users\"],\n    summary=\"Update user\",\n    responses={\n        400: {\"model\": ErrorResponse},\n        404: {\"model\": ErrorResponse},\n    },\n)\nasync def update_user(\n    user_id: UUID = Path(..., description=\"User ID\"),\n    user: UserUpdate = ...,\n):\n    \"\"\"Update user attributes.\"\"\"\n    pass\n\n@app.delete(\n    \"\u002Fusers\u002F{user_id}\",\n    status_code=204,\n    tags=[\"Users\", \"Admin\"],\n    summary=\"Delete user\",\n    responses={404: {\"model\": ErrorResponse}},\n)\nasync def delete_user(\n    user_id: UUID = Path(..., description=\"User ID\"),\n):\n    \"\"\"Permanently delete a user.\"\"\"\n    pass\n\n# Export OpenAPI spec\nif __name__ == \"__main__\":\n    import json\n    print(json.dumps(app.openapi(), indent=2))\n```\n\n### Template 3: Code-First (TypeScript\u002FExpress with tsoa)\n\n```typescript\n\u002F\u002F tsoa generates OpenAPI from TypeScript decorators\n\nimport {\n  Controller,\n  Get,\n  Post,\n  Patch,\n  Delete,\n  Route,\n  Path,\n  Query,\n  Body,\n  Response,\n  SuccessResponse,\n  Tags,\n  Security,\n  Example,\n} from \"tsoa\";\n\n\u002F\u002F Models\ninterface User {\n  \u002F** Unique identifier *\u002F\n  id: string;\n  \u002F** User email address *\u002F\n  email: string;\n  \u002F** Display name *\u002F\n  name: string;\n  status: UserStatus;\n  role: UserRole;\n  \u002F** Avatar URL *\u002F\n  avatar?: string;\n  \u002F** Custom metadata *\u002F\n  metadata?: Record\u003Cstring, unknown>;\n  createdAt: Date;\n  updatedAt?: Date;\n}\n\nenum UserStatus {\n  Active = \"active\",\n  Inactive = \"inactive\",\n  Suspended = \"suspended\",\n  Pending = \"pending\",\n}\n\nenum UserRole {\n  User = \"user\",\n  Moderator = \"moderator\",\n  Admin = \"admin\",\n}\n\ninterface CreateUserRequest {\n  email: string;\n  name: string;\n  role?: UserRole;\n  metadata?: Record\u003Cstring, unknown>;\n}\n\ninterface UpdateUserRequest {\n  name?: string;\n  status?: UserStatus;\n  role?: UserRole;\n  metadata?: Record\u003Cstring, unknown>;\n}\n\ninterface Pagination {\n  page: number;\n  limit: number;\n  total: number;\n  totalPages: number;\n  hasNext: boolean;\n  hasPrev: boolean;\n}\n\ninterface UserListResponse {\n  data: User[];\n  pagination: Pagination;\n}\n\ninterface ErrorResponse {\n  code: string;\n  message: string;\n  details?: { field: string; message: string }[];\n  requestId?: string;\n}\n\n@Route(\"users\")\n@Tags(\"Users\")\nexport class UsersController extends Controller {\n  \u002F**\n   * List all users with pagination and filtering\n   * @param page Page number (1-based)\n   * @param limit Items per page (max 100)\n   * @param status Filter by user status\n   * @param search Search by name or email\n   *\u002F\n  @Get()\n  @Security(\"bearerAuth\")\n  @Response\u003CErrorResponse>(400, \"Invalid request\")\n  @Response\u003CErrorResponse>(401, \"Unauthorized\")\n  @Example\u003CUserListResponse>({\n    data: [\n      {\n        id: \"550e8400-e29b-41d4-a716-446655440000\",\n        email: \"john@example.com\",\n        name: \"John Doe\",\n        status: UserStatus.Active,\n        role: UserRole.User,\n        createdAt: new Date(\"2024-01-15T10:30:00Z\"),\n      },\n    ],\n    pagination: {\n      page: 1,\n      limit: 20,\n      total: 1,\n      totalPages: 1,\n      hasNext: false,\n      hasPrev: false,\n    },\n  })\n  public async listUsers(\n    @Query() page: number = 1,\n    @Query() limit: number = 20,\n    @Query() status?: UserStatus,\n    @Query() search?: string\n  ): Promise\u003CUserListResponse> {\n    \u002F\u002F Implementation\n    throw new Error(\"Not implemented\");\n  }\n\n  \u002F**\n   * Create a new user\n   *\u002F\n  @Post()\n  @Security(\"bearerAuth\")\n  @SuccessResponse(201, \"Created\")\n  @Response\u003CErrorResponse>(400, \"Invalid request\")\n  @Response\u003CErrorResponse>(409, \"Email already exists\")\n  public async createUser(\n    @Body() body: CreateUserRequest\n  ): Promise\u003CUser> {\n    this.setStatus(201);\n    throw new Error(\"Not implemented\");\n  }\n\n  \u002F**\n   * Get user by ID\n   * @param userId User ID\n   *\u002F\n  @Get(\"{userId}\")\n  @Security(\"bearerAuth\")\n  @Response\u003CErrorResponse>(404, \"User not found\")\n  public async getUser(\n    @Path() userId: string\n  ): Promise\u003CUser> {\n    throw new Error(\"Not implemented\");\n  }\n\n  \u002F**\n   * Update user attributes\n   * @param userId User ID\n   *\u002F\n  @Patch(\"{userId}\")\n  @Security(\"bearerAuth\")\n  @Response\u003CErrorResponse>(400, \"Invalid request\")\n  @Response\u003CErrorResponse>(404, \"User not found\")\n  public async updateUser(\n    @Path() userId: string,\n    @Body() body: UpdateUserRequest\n  ): Promise\u003CUser> {\n    throw new Error(\"Not implemented\");\n  }\n\n  \u002F**\n   * Delete user\n   * @param userId User ID\n   *\u002F\n  @Delete(\"{userId}\")\n  @Tags(\"Users\", \"Admin\")\n  @Security(\"bearerAuth\")\n  @SuccessResponse(204, \"Deleted\")\n  @Response\u003CErrorResponse>(404, \"User not found\")\n  public async deleteUser(\n    @Path() userId: string\n  ): Promise\u003Cvoid> {\n    this.setStatus(204);\n  }\n}\n```\n\n### Template 4: Validation & Linting\n\n```bash\n# Install validation tools\nnpm install -g @stoplight\u002Fspectral-cli\nnpm install -g @redocly\u002Fcli\n\n# Spectral ruleset (.spectral.yaml)\ncat > .spectral.yaml \u003C\u003C 'EOF'\nextends: [\"spectral:oas\", \"spectral:asyncapi\"]\n\nrules:\n  # Enforce operation IDs\n  operation-operationId: error\n\n  # Require descriptions\n  operation-description: warn\n  info-description: error\n\n  # Naming conventions\n  operation-operationId-valid-in-url: true\n\n  # Security\n  operation-security-defined: error\n\n  # Response codes\n  operation-success-response: error\n\n  # Custom rules\n  path-params-snake-case:\n    description: Path parameters should be snake_case\n    severity: warn\n    given: \"$.paths[*].parameters[?(@.in == 'path')].name\"\n    then:\n      function: pattern\n      functionOptions:\n        match: \"^[a-z][a-z0-9_]*$\"\n\n  schema-properties-camelCase:\n    description: Schema properties should be camelCase\n    severity: warn\n    given: \"$.components.schemas[*].properties[*]~\"\n    then:\n      function: casing\n      functionOptions:\n        type: camel\nEOF\n\n# Run Spectral\nspectral lint openapi.yaml\n\n# Redocly config (redocly.yaml)\ncat > redocly.yaml \u003C\u003C 'EOF'\nextends:\n  - recommended\n\nrules:\n  no-invalid-media-type-examples: error\n  no-invalid-schema-examples: error\n  operation-4xx-response: warn\n  request-mime-type:\n    severity: error\n    allowedValues:\n      - application\u002Fjson\n  response-mime-type:\n    severity: error\n    allowedValues:\n      - application\u002Fjson\n      - application\u002Fproblem+json\n\ntheme:\n  openapi:\n    generateCodeSamples:\n      languages:\n        - lang: curl\n        - lang: python\n        - lang: javascript\nEOF\n\n# Run Redocly\nredocly lint openapi.yaml\nredocly bundle openapi.yaml -o bundled.yaml\nredocly preview-docs openapi.yaml\n```\n\n## SDK Generation\n\n```bash\n# OpenAPI Generator\nnpm install -g @openapitools\u002Fopenapi-generator-cli\n\n# Generate TypeScript client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g typescript-fetch \\\n  -o .\u002Fgenerated\u002Ftypescript-client \\\n  --additional-properties=supportsES6=true,npmName=@myorg\u002Fapi-client\n\n# Generate Python client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g python \\\n  -o .\u002Fgenerated\u002Fpython-client \\\n  --additional-properties=packageName=api_client\n\n# Generate Go client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g go \\\n  -o .\u002Fgenerated\u002Fgo-client\n```\n\n## Best Practices\n\n### Do's\n- **Use $ref** - Reuse schemas, parameters, responses\n- **Add examples** - Real-world values help consumers\n- **Document errors** - All possible error codes\n- **Version your API** - In URL or header\n- **Use semantic versioning** - For spec changes\n\n### Don'ts\n- **Don't use generic descriptions** - Be specific\n- **Don't skip security** - Define all schemes\n- **Don't forget nullable** - Be explicit about null\n- **Don't mix styles** - Consistent naming throughout\n- **Don't hardcode URLs** - Use server variables\n\n## Resources\n\n- [OpenAPI 3.1 Specification](https:\u002F\u002Fspec.openapis.org\u002Foas\u002Fv3.1.0)\n- [Swagger Editor](https:\u002F\u002Feditor.swagger.io\u002F)\n- [Redocly](https:\u002F\u002Fredocly.com\u002F)\n- [Spectral](https:\u002F\u002Fstoplight.io\u002Fopen-source\u002Fspectral)\n",{"name":5,"description":7},"main","7850740bd837adb32f0a109ab2af82482196c6f2","skills\u002Fwshobson-agents\u002Fplugins\u002Fdocumentation-generation\u002Fskills\u002Fopenapi-spec-generation",1,"https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills","https:\u002F\u002Fzturnsgo.com","https:\u002F\u002Fgithub.com\u002Fshengdabai","https:\u002F\u002Fzturnsgo.com\u002F","AI × education creator — producer, not consumer. Taught 6,000+ learners; shipping learning tools & daily essays with Claude Code, practicing @xiaolai's way 🥋","2026-11-01 21:28:23",{"source":8,"skillName":5,"commands":68,"prompt":78,"zipAvailable":79},[69,72,75],{"tool":70,"command":71},"npx","npx skills add shengdabai\u002FTony-Claude-Code-Skills --skill openapi-spec-generation",{"tool":73,"command":74},"bunx","bunx skills add shengdabai\u002FTony-Claude-Code-Skills --skill openapi-spec-generation",{"tool":76,"command":77},"pnpm","pnpm dlx skills add shengdabai\u002FTony-Claude-Code-Skills --skill openapi-spec-generation","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fwshobson-agents\u002Fplugins\u002Fdocumentation-generation\u002Fskills\u002Fopenapi-spec-generation\u002FSKILL.md",true,[],[82,99,126,151,172,192],{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":91,"publishedAt":52},"01M70P17DAD4C1C47NPH6A96EM","xiaolai-methodology","以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 \"李笑来\"、\"笑来\"、\"笑来老师\"、\"扮演李笑来\"、\"找笑来聊聊\"、\"笑来怎么看\"、\"xiaolai\"、\"把时间当作朋友\"、\"通往财富自由之路\"、\"定投改变命运\"、\"韭菜的自我修养\"、\"自学是门手艺\"、\"让时间陪你慢慢变富\"、\"七年就是一辈子\"、\"元认知\"、\"活在未来\"、\"复利\u002F长期主义\"、\"践行\"、\"痴迷改进\"、\"个人商业模式\u002F出售时间\"、\"睡后收入\"、\"定投\"、\"财富自由\"、\"操作系统升级\"、\"最少必要知识\"、\"北极星提示词\"、\"用 AI 该怎么学\u002F思考\" 等任一触发词,或当用户带着关于创业\u002F赚钱\u002F投资\u002F学习\u002F职业\u002F重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fxiaolai-methodology\u002FSKILL.md",[88],{"categoryNo":89,"categoryName":90,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",[92,93,94,95,96,97,98],"methodology","xiaolai","xiaolai methodology","朋友","投资","writing","收入",{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":109,"publishedAt":52},"01M70P13NB9YP7ACJR1S861CDS","zread","Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `.\u002F.zread\u002Fwiki\u002F`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module\u002Fpackage explanations, or \"what does this repo do\"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread \u002F zread.ai directly. Also use it proactively before diving into a large unknown repo — if `.\u002F.zread\u002Fwiki\u002Fcurrent` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word \"zread\".","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fzread\u002FSKILL.md",[105,108],{"categoryNo":106,"categoryName":107,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[101,110,111,112,113,114,115,116,117,39,118,119,120,41,121,122,123,44,124,125],"knowledge","wiki","explore","walkthrough","project","get","knowledge base","run","intent","cli","map","project wiki","disk","architecture","unknown","generated",{"skillNo":127,"name":128,"nameZh":6,"description":129,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":130,"categories":131,"keywords":136,"publishedAt":52},"01M70P10S4SWMZQ8BJAFBFDRW3","youtube","Launch the YouTube Creator Agent to auto-generate videos.\nUse when: user types \u002Fyoutube, wants to create YouTube videos,\ngenerate video content, or upload to YouTube.\nKeywords: youtube, video, 视频, 创作视频, 发布视频","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube\u002FSKILL.md",[132,133],{"categoryNo":106,"categoryName":107,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":134,"categoryName":135,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000036","内容创作者",[128,137,138,139,140,141,142,143,39,144,145,146,45,46,147,148,149,150],"user","content","视频","creator","generate video","videos","launch","video content","create","upload","auto","types","Video","创作",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":158,"publishedAt":52},"01M70P0YE95D8BACZCGV8BK90K","youtube-script-sync","Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新\u002F同步\u002F优化 Youtube视频逐字稿知识库”, or says “更新\u002F同步\u002F优化该知识库” while this knowledge base is the active context.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-sync\u002FSKILL.md",[157],{"categoryNo":106,"categoryName":107,"taxonomyType":21,"taxonomyTypeDesc":22},[159,160,128,161,162,149,163,40,164,165,166,167,168,169,116,170,45,46,110,171],"sync","script","youtube script sync","recording","shorts","base","person","update","active","child","optimize","idempotent","long",{"skillNo":173,"name":174,"nameZh":6,"description":175,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":176,"categories":177,"keywords":182,"publishedAt":52},"01M70P0WMV5XT0EEC0N445PW07","writing-skills","How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-skills\u002FSKILL.md",[178,179,180,181],{"categoryNo":89,"categoryName":90,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":106,"categoryName":107,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},{"categoryNo":134,"categoryName":135,"taxonomyType":26,"taxonomyTypeDesc":27},[183,97,184,185,186,187,44,45,46,188,189,190,191],"writing skills","how","teach","write skill","write","claude code skills","files","how to","trigger",{"skillNo":193,"name":194,"nameZh":6,"description":195,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":196,"categories":197,"keywords":200,"publishedAt":52},"01M70P0SJFR4YVJQBGMHKGSKXT","writing-rules","How to write .claude\u002Frules\u002F files that Claude actually follows. Use when creating, improving, or reviewing project rules.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-rules\u002FSKILL.md",[198,199],{"categoryNo":89,"categoryName":90,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":134,"categoryName":135,"taxonomyType":26,"taxonomyTypeDesc":27},[201,97,202,114,203,189,45,184,187,46,190],"Rules","writing rules","project rules"]