openapi: 3.1.0
info:
  title: SkillMarket API
  version: 1.0.0
  summary: Agent Skills marketplace HTTP API
  description: |
    Public API for integrating SkillMarket with other systems.

    Capabilities:
    - Search and list skills
    - Upload / publish a skill package
    - Fetch skill detail
    - Resolve a download URL for the latest skill package

    Authentication is **not required** in this phase.
  contact:
    name: SkillMarket
license:
  name: Proprietary

servers:
  - url: http://localhost:3000
    description: Local development
  - url: /
    description: Current host

tags:
  - name: Skills
    description: Discover and download Agent Skills

paths:
  /api/skills:
    get:
      tags: [Skills]
      operationId: listSkills
      summary: List or search skills
      description: |
        Returns a paginated skill list for table/list UIs.
        Use `q` for keyword search across slug, display name, summary, and category.
        Use `tab` to switch discovery views (trending / featured / official / new).
      parameters:
        - $ref: "#/components/parameters/Tab"
        - $ref: "#/components/parameters/Query"
        - $ref: "#/components/parameters/Sort"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Skill list page
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SkillListResult"
              examples:
                trending:
                  summary: Trending skills
                  value:
                    items:
                      - slug: code-reviewer
                        displayName: code-reviewer
                        summary: Review pull requests for bugs and maintainability.
                        category: engineering
                        topics: [review, quality]
                        isFeatured: true
                        isOfficial: true
                        stats:
                          downloads: 12840
                          installs: 3200
                          stars: 890
                          versions: 1
                        latestVersion: "1.2.0"
                        publisher:
                          handle: skills-lab
                          name: Skills Lab
                        createdAt: "2025-11-02T08:00:00.000Z"
                        updatedAt: "2026-07-18T10:00:00.000Z"
                    total: 1
                    page: 1
                    pageSize: 50
        "400":
          $ref: "#/components/responses/BadRequest"

    post:
      tags: [Skills]
      operationId: uploadSkill
      summary: Upload / publish a skill
      description: |
        Upload a new skill or a new version of an existing skill.

        Multipart fields:
        - `name` (required): skill display name (also used to derive slug)
        - `description` (required): short skill description / summary
        - `package` (required): zip file that includes `SKILL.md`
        - `version` (optional): semver, default `1.0.0`
        - `category`, `license`, `changelog` (optional)

        No authentication is required in this phase.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/SkillUploadRequest"
            encoding:
              package:
                contentType: application/zip
      responses:
        "201":
          description: Skill created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SkillUploadResponse"
        "200":
          description: New version added to an existing skill
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SkillUploadResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "409":
          description: Skill version already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error: Skill "code-reviewer" version 1.0.0 already exists

  /api/skills/{slug}:
    get:
      tags: [Skills]
      operationId: getSkillBySlug
      summary: Get skill detail
      description: Returns full skill metadata, SKILL.md content, and version history.
      parameters:
        - $ref: "#/components/parameters/Slug"
      responses:
        "200":
          description: Skill detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SkillDetailResponse"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/skills/{slug}/download-url:
    get:
      tags: [Skills]
      operationId: getSkillDownloadUrl
      summary: Get skill package download URL
      description: |
        Resolves the latest package download link for other systems.
        The returned `downloadUrl` points to `/api/skills/{slug}/download`,
        which streams the zip package (`SKILL.md` required inside).
        This endpoint does **not** increment download counters; counting happens
        when the download endpoint is actually fetched.
      parameters:
        - $ref: "#/components/parameters/Slug"
        - name: version
          in: query
          required: false
          description: Optional semver. Defaults to the latest version.
          schema:
            type: string
            example: "1.2.0"
      responses:
        "200":
          description: Download URL payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SkillDownloadUrlResponse"
              examples:
                latest:
                  value:
                    slug: code-reviewer
                    version: "1.2.0"
                    filename: code-reviewer-1.2.0.zip
                    contentType: application/zip
                    fileSize: 770
                    sha256: "ab12..."
                    license: MIT
                    downloadUrl: http://localhost:3000/api/skills/code-reviewer/download
                    downloadPath: /api/skills/code-reviewer/download
        "404":
          $ref: "#/components/responses/NotFound"

  /api/skills/{slug}/download:
    get:
      tags: [Skills]
      operationId: downloadSkillPackage
      summary: Download skill package zip
      description: |
        Streams the skill zip package as an attachment and increments the
        skill download counter. Prefer resolving the URL via
        `/download-url` first when integrating from other systems.
      parameters:
        - $ref: "#/components/parameters/Slug"
        - name: version
          in: query
          required: false
          description: Optional semver. Defaults to the latest version.
          schema:
            type: string
            example: "1.2.0"
      responses:
        "200":
          description: Zip package binary
          headers:
            Content-Disposition:
              schema:
                type: string
              example: attachment; filename="code-reviewer-1.2.0.zip"
          content:
            application/zip:
              schema:
                type: string
                format: binary
        "404":
          $ref: "#/components/responses/NotFound"
        "502":
          description: Upstream object storage failure
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/openapi:
    get:
      tags: [Skills]
      operationId: getOpenApiDocument
      summary: Fetch OpenAPI document
      description: Returns this OpenAPI specification as JSON (default) or YAML.
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            enum: [json, yaml]
            default: json
      responses:
        "200":
          description: OpenAPI document
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
            application/yaml:
              schema:
                type: string

components:
  parameters:
    Slug:
      name: slug
      in: path
      required: true
      description: Skill slug identifier
      schema:
        type: string
        pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$"
        example: code-reviewer
    Tab:
      name: tab
      in: query
      required: false
      description: Discovery tab / collection
      schema:
        $ref: "#/components/schemas/SkillTab"
        default: trending
    Query:
      name: q
      in: query
      required: false
      description: Keyword search
      schema:
        type: string
        example: sql
    Sort:
      name: sort
      in: query
      required: false
      description: Sort field for list views
      schema:
        $ref: "#/components/schemas/SkillSort"
        default: popularity
    Page:
      name: page
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        default: 1
    PageSize:
      name: pageSize
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 50

  schemas:
    SkillTab:
      type: string
      enum: [trending, featured, official, new]
    SkillSort:
      type: string
      enum: [name, category, popularity]
    SkillStats:
      type: object
      additionalProperties: false
      required: [downloads, installs, stars, versions]
      properties:
        downloads:
          type: integer
          minimum: 0
        installs:
          type: integer
          minimum: 0
        stars:
          type: integer
          minimum: 0
        versions:
          type: integer
          minimum: 0
        comments:
          type: integer
          minimum: 0
    Publisher:
      type: object
      additionalProperties: false
      required: [handle, name]
      properties:
        handle:
          type: string
        name:
          type: string
    SkillListItem:
      type: object
      additionalProperties: false
      required:
        - slug
        - displayName
        - summary
        - category
        - topics
        - isFeatured
        - isOfficial
        - stats
        - latestVersion
        - publisher
        - createdAt
        - updatedAt
      properties:
        slug:
          type: string
        displayName:
          type: string
        summary:
          type: string
        category:
          type: ["string", "null"]
        topics:
          type: array
          items:
            type: string
        isFeatured:
          type: boolean
        isOfficial:
          type: boolean
        stats:
          $ref: "#/components/schemas/SkillStats"
        latestVersion:
          type: ["string", "null"]
        publisher:
          $ref: "#/components/schemas/Publisher"
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    SkillVersion:
      type: object
      additionalProperties: false
      required: [version, changelog, license, fileSize, sha256, createdAt]
      properties:
        version:
          type: string
        changelog:
          type: ["string", "null"]
        license:
          type: ["string", "null"]
        fileSize:
          type: integer
          minimum: 0
        sha256:
          type: ["string", "null"]
        createdAt:
          type: string
          format: date-time
    SkillDetail:
      allOf:
        - $ref: "#/components/schemas/SkillListItem"
        - type: object
          additionalProperties: false
          required: [description, versions]
          properties:
            description:
              type: string
              description: Full SKILL.md content (including frontmatter)
            versions:
              type: array
              items:
                $ref: "#/components/schemas/SkillVersion"
    SkillListResult:
      type: object
      additionalProperties: false
      required: [items, total, page, pageSize]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/SkillListItem"
        total:
          type: integer
          minimum: 0
        page:
          type: integer
          minimum: 1
        pageSize:
          type: integer
          minimum: 1
    SkillDetailResponse:
      type: object
      additionalProperties: false
      required: [skill]
      properties:
        skill:
          $ref: "#/components/schemas/SkillDetail"
    SkillUploadRequest:
      type: object
      additionalProperties: false
      required: [name, description, package]
      properties:
        name:
          type: string
          description: Skill display name
          example: code-reviewer
        description:
          type: string
          description: Short skill description / summary
          example: Review pull requests for bugs and maintainability.
        package:
          type: string
          format: binary
          description: Zip package containing SKILL.md
        version:
          type: string
          description: Semver version (default 1.0.0)
          example: "1.0.0"
        category:
          type: string
          example: engineering
        license:
          type: string
          example: MIT
        changelog:
          type: string
          example: Initial public release.
    SkillUploadResponse:
      type: object
      additionalProperties: false
      required: [skill, created, version, downloadPath]
      properties:
        skill:
          $ref: "#/components/schemas/SkillDetail"
        created:
          type: boolean
          description: true when a new skill slug was created
        version:
          type: string
        downloadPath:
          type: string
          example: /api/skills/code-reviewer/download
    SkillDownloadUrlResponse:
      type: object
      additionalProperties: false
      required:
        - slug
        - version
        - filename
        - contentType
        - fileSize
        - sha256
        - license
        - downloadUrl
        - downloadPath
      properties:
        slug:
          type: string
        version:
          type: string
        filename:
          type: string
        contentType:
          type: string
          example: application/zip
        fileSize:
          type: integer
          minimum: 0
        sha256:
          type: ["string", "null"]
        license:
          type: ["string", "null"]
        downloadUrl:
          type: string
          format: uri
          description: Absolute URL other systems can fetch
        downloadPath:
          type: string
          description: Path-only download endpoint
    ErrorResponse:
      type: object
      additionalProperties: false
      required: [error]
      properties:
        error:
          type: string

  responses:
    BadRequest:
      description: Invalid request parameters
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    NotFound:
      description: Skill not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error: Skill not found

security: []
