{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://skill-map.ai/spec/v1/extensions/provider-kind.schema.json",
  "title": "ProviderKindMetadata",
  "description": "Per-kind UI metadata written as `<plugin>/kinds/<kindName>/kind.json`. Lives next to the kind's frontmatter `schema.json` under the kind folder; together they are the structure-as-truth replacement for the old `kinds` map inside the Provider manifest. Reaches the UI via the `kindRegistry` field embedded in REST envelopes (`api/rest-envelope.schema.json`). The kind name is the folder name; it is NOT repeated as a field here.",
  "type": "object",
  "required": ["ui"],
  "additionalProperties": false,
  "properties": {
    "identifiers": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "string",
        "enum": ["frontmatter.name", "filename-basename", "dirname"]
      },
      "description": "Priority-ordered identifier sources the post-walk resolver consults to derive this kind's canonical name(s); order is meaningful (the resolver visits sources in declaration order, and the resulting name index is presence-based so multiple matches collapse). Each entry contributes one normalised name to the resolution index, so a node of this kind becomes name-resolvable (an `invokes` / reference link naming it can resolve to it). Absent means the kind is not name-resolvable. Sources: `frontmatter.name` reads `frontmatter.name` (required-name kinds like agent / command / skill declare this first); `filename-basename` is `basename(path)` without extension (the filename IS the handle when `name:` is absent); `dirname` is the parent directory name (skills resolve to the folder between the skills root and `SKILL.md`)."
    },
    "identifierMismatch": {
      "type": "string",
      "enum": ["warn", "info"],
      "description": "Severity of the `core/name-mismatch` issue emitted when a node's normalised `frontmatter.name` diverges from a declared path-derived identifier (`filename-basename` / `dirname`), giving the node two live names in the resolution index. Absent means no diagnostic. Use `warn` when the kind's standard REQUIRES agreement (e.g. the open-standard skill kind mandates name == parent dirname); `info` when the runtime documents the divergence as a legal override yet the dual identity is still worth surfacing."
    },
    "ui": {
      "type": "object",
      "required": ["label", "color"],
      "additionalProperties": false,
      "description": "Presentation metadata the UI uses to render nodes of this kind (palette swatches, list tags, graph nodes, filter chips). Required so the UI never has to invent visuals for a kind a Provider declares. The Provider declares intent (label + base color, optional dark variant + emoji + icon); the UI derives bg/fg tints from `color` per theme via a deterministic helper.",
      "properties": {
        "label": {
          "type": "string",
          "minLength": 1,
          "description": "Plural human-readable label for groups of this kind (e.g. `'Skills'`, `'Agents'`, `'Cursor Rules'`). Used in filter dropdowns, palette tooltips, and any list grouping."
        },
        "color": {
          "type": "string",
          "pattern": "^#[0-9a-fA-F]{6}$",
          "description": "Base hex color (light theme). The UI derives `bg` and `fg` tints from this value at runtime; declaring a single base value (instead of three) keeps the manifest small and lets the UI control accessibility-driven contrast."
        },
        "colorDark": {
          "type": "string",
          "pattern": "^#[0-9a-fA-F]{6}$",
          "description": "Optional dark-theme variant of `color`. When absent, the UI falls back to `color`. Declared explicitly because a luminosity flip rarely matches the brand intent for kinds that should stand out in dark mode."
        },
        "emoji": {
          "type": "string",
          "minLength": 1,
          "maxLength": 8,
          "description": "Optional decorative emoji used as a fallback when `icon` is absent or fails to render. Bound to a small length so the UI can lay it out predictably alongside text."
        },
        "icon": {
          "description": "Optional discriminated icon descriptor. The UI prefers `icon` over `emoji`; when both are absent, the UI falls back to the first letter of `label` colored with `color`.",
          "oneOf": [
            {
              "type": "object",
              "required": ["kind", "id"],
              "additionalProperties": false,
              "properties": {
                "kind": { "const": "pi" },
                "id": {
                  "type": "string",
                  "pattern": "^pi-[a-z0-9]+(-[a-z0-9]+)*$",
                  "description": "PrimeIcons identifier (e.g. `pi-cog`, `pi-bolt`). Matched verbatim against the `pi pi-<id>` class the UI emits."
                }
              }
            },
            {
              "type": "object",
              "required": ["kind", "path"],
              "additionalProperties": false,
              "properties": {
                "kind": { "const": "svg" },
                "path": {
                  "type": "string",
                  "minLength": 1,
                  "description": "Raw SVG path data (the `d` attribute of one or more `<path>` elements, joined). The UI wraps it in `<svg viewBox=\"0 0 24 24\"><path d=\"...\"/></svg>` and tints it with `currentColor`."
                }
              }
            }
          ]
        }
      }
    }
  }
}
