{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://petty.ericli.tech/plugin-manifest.schema.json",
  "title": "Petty Plugin Manifest",
  "description": "Schema for manifest.json in a Petty plugin folder. Reference this schema by adding \"$schema\": \"https://petty.ericli.tech/plugin-manifest.schema.json\" at the top of your manifest. Vocabulary is kept in sync with PluginManifest.swift; see https://petty.ericli.tech/docs/guide/manifest for prose docs.",
  "type": "object",
  "required": ["id", "name", "script"],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "format": "uri",
      "description": "Reference to this schema for editor autocomplete and validation."
    },
    "id": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[A-Za-z0-9_-]+$",
      "description": "Unique plugin ID. Must match the folder name. ASCII alphanumerics, hyphens, and underscores only — Unicode lookalikes are rejected at load time to prevent visually-deceptive IDs."
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "description": "Human-readable name shown in Settings → Plugins."
    },
    "author": {
      "type": "string",
      "description": "Author or organization name."
    },
    "version": {
      "type": "string",
      "description": "Semver version string (e.g. \"1.0.0\"). Not enforced — used for display only."
    },
    "description": {
      "type": "string",
      "description": "One-line description shown in the plugin list."
    },
    "homepage": {
      "type": "string",
      "format": "uri",
      "description": "URL to the plugin's homepage or repository."
    },
    "script": {
      "type": "string",
      "minLength": 1,
      "description": "Path to the JS entry point, relative to the plugin folder. Must resolve inside the directory — no parent-traversal segments."
    },
    "icon": {
      "type": "string",
      "description": "Path to an icon image, relative to the plugin folder."
    },
    "capabilities": {
      "type": "array",
      "default": [],
      "uniqueItems": true,
      "description": "What the plugin does. Some capabilities (tts-provider, file-actions) gate access to a corresponding petty.* namespace; others are informational tags used by the marketplace.",
      "items": {
        "type": "string",
        "anyOf": [
          {
            "enum": ["tts-provider", "file-actions"],
            "description": "Capabilities that gate a petty.* namespace."
          },
          {
            "enum": ["quips", "watchers", "reactions", "menu-items"],
            "description": "Informational tags retained for backward compatibility with community plugins."
          }
        ]
      }
    },
    "permissions": {
      "type": "array",
      "default": [],
      "uniqueItems": true,
      "description": "What system access the plugin needs. Each gates a petty.* namespace; manifests declaring an unknown permission are rejected at load time.",
      "items": {
        "type": "string",
        "anyOf": [
          {
            "enum": ["calendar", "weather", "clipboard", "active-app", "network", "exec"],
            "description": "Permissions that gate a petty.* namespace."
          },
          {
            "enum": ["system", "storage"],
            "description": "Always-on namespaces; declared for documentation purposes."
          }
        ]
      }
    },
    "exec": {
      "type": "object",
      "description": "Required when permissions includes \"exec\". Bounds which commands the plugin may shell out to.",
      "additionalProperties": false,
      "required": ["allowlist"],
      "properties": {
        "allowlist": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "description": "Commands this plugin is allowed to run. Non-empty.",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "timeout": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "Default timeout in milliseconds for exec calls. Falls back to 300000 (5 min) if unspecified."
        }
      }
    },
    "rateLimits": {
      "type": "object",
      "description": "Named cooldowns consumed by petty.rateLimit. Each id is scoped to this plugin and persists across restarts.",
      "additionalProperties": {
        "type": "string",
        "pattern": "^[0-9]+(\\.[0-9]+)?[smhd]$",
        "description": "Duration string with unit suffix: \"30s\", \"5m\", \"2h\", \"1d\"."
      }
    },
    "settings": {
      "type": "array",
      "description": "Declarative settings the host renders in Settings → Plugins → <plugin>. Plugins read values back via petty.settings.get(key).",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["key", "type", "label"],
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "description": "Storage key. Stable identifier — renaming sheds saved values."
          },
          "type": {
            "type": "string",
            "enum": ["bool", "number", "string", "enum"]
          },
          "label": {
            "type": "string",
            "description": "Display label."
          },
          "description": {
            "type": "string",
            "description": "Help text shown beneath the control."
          },
          "default": {
            "description": "Initial value. Type must match `type`.",
            "type": ["boolean", "number", "string"]
          },
          "min": {
            "type": "number",
            "description": "Minimum value (number type only)."
          },
          "max": {
            "type": "number",
            "description": "Maximum value (number type only)."
          },
          "step": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Step size (number type only)."
          },
          "options": {
            "type": "array",
            "minItems": 1,
            "description": "Required when type is \"enum\". Each option binds a stored value to a display label.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": ["value", "label"],
              "properties": {
                "value": { "type": "string" },
                "label": { "type": "string" }
              }
            }
          }
        },
        "allOf": [
          {
            "if": { "properties": { "type": { "const": "enum" } }, "required": ["type"] },
            "then": { "required": ["options"] }
          }
        ]
      }
    }
  },
  "allOf": [
    {
      "$comment": "exec permission requires the exec block.",
      "if": {
        "properties": {
          "permissions": {
            "type": "array",
            "contains": { "const": "exec" }
          }
        },
        "required": ["permissions"]
      },
      "then": { "required": ["exec"] }
    }
  ]
}
