{
  "schema": "snappy-card/1",
  "skill": "snappy-thumbnails",
  "tier": "public",
  "origin_tier": "base",
  "purpose": "Turn an approved video title and hook into a concrete thumbnail brief, generate local `1280x720` variants through `snappy-image`, list what exists, stage `thumbnails.set` through `snappy-youtube`, and audit recent channel thumbnails as counts. Read [SKILL.md](SKILL.md) for doctrine, source research, safe zones, and review criteria. Exact style prompts live in `prompts/`.",
  "verbs": [
    {
      "name": "audit",
      "args": [
        "channel?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-thumbnails/api.ts audit"
    },
    {
      "name": "brief",
      "args": [
        "title",
        "hook"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-thumbnails/api.ts brief \"<title>\" <hook>"
    },
    {
      "name": "generate",
      "args": [
        "brief-file",
        "count?"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-thumbnails/api.ts generate <brief-file>"
    },
    {
      "name": "set",
      "args": [
        "video-id",
        "file"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-thumbnails/api.ts set <video-id> <file>"
    },
    {
      "name": "variants",
      "args": [
        "slug"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-thumbnails/api.ts variants <slug>"
    }
  ],
  "faces": [],
  "agents": [],
  "uses": [],
  "used_by": [],
  "checks": {
    "pass": 42,
    "fail": 11,
    "defer": 8,
    "line": "42 of 53 checks pass",
    "failing": [
      {
        "id": 5,
        "rule": "Description is valid, third-person, and says what plus when",
        "says": "no when/use marker"
      },
      {
        "id": 7,
        "rule": "Promised and declared verbs have no set difference",
        "says": "promised-only=[contract]; loader-missing=[]"
      },
      {
        "id": 8,
        "rule": "Skill body and reference graph stay within disclosure budgets",
        "says": "348 lines; ~5194 tokens"
      },
      {
        "id": 10,
        "rule": "One term names each concept",
        "says": "mixed synonym sets: endpoint/url/route/path"
      },
      {
        "id": 11,
        "rule": "Each task presents one default, not a menu",
        "says": "2 line(s) present three-way alternative menus"
      },
      {
        "id": 24,
        "rule": "Answer size is bounded by requested limit",
        "says": "unbounded growth: audit:non-monotone"
      },
      {
        "id": 31,
        "rule": "Refusals have outcome, code, message, and fix",
        "says": "refusals schema-checked for outcome/code/message/fix"
      },
      {
        "id": 32,
        "rule": "Refusals name a literal from the valid domain",
        "says": "refusals checked against contract literals"
      },
      {
        "id": 40,
        "rule": "Metadata, skill body, and resources follow three-level budgets",
        "says": "metadata ~69 tokens; body ~5194 tokens"
      },
      {
        "id": 55,
        "rule": "Eval scenarios are multi-call with ground truth",
        "says": "eval manifest missing"
      },
      {
        "id": 57,
        "rule": "Eval manifest contains a disjoint holdout",
        "says": "0 disjoint holdout scenario(s)"
      }
    ]
  },
  "coverage": null,
  "spec": null,
  "evals": null,
  "files": [
    "AGENTS.md",
    "SKILL.md",
    "api.test.ts",
    "api.ts",
    "faces/family.tsx",
    "faces/fixtures/thumbnails-set.json",
    "prompts/face-led.md",
    "prompts/product-led.md",
    "prompts/type-led.md",
    "refusals.test.ts"
  ],
  "total_size": 67814,
  "checksum": "sha256:68912345f9096ed4986fdccdd958ca94fd8a4848093b536c50378dc02df3ac92",
  "built_at": "2026-09-09T22:11:14.088Z",
  "openapi": {
    "openapi": "3.1.0",
    "info": {
      "title": "snappy-thumbnails",
      "version": "0.0.0",
      "description": "Master YouTube thumbnail operating manual and typed workflow: turn an approved title and hook into a brief, generate variants through snappy-image, inventory local variants, stage YouTube thumbnails.set through snappy-youtube, and audit recent channel thumbnails by counts."
    },
    "x-snappy-transport": "cli",
    "servers": [
      {
        "url": "npx tsx ~/.claude/skills/snappy-thumbnails/api.ts",
        "description": "The hand itself. Every path is a verb word, every query or body key an argument."
      }
    ],
    "paths": {
      "/audit": {
        "get": {
          "operationId": "snappy_thumbnails_audit",
          "summary": "snappy-thumbnails audit",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "channel",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string",
                "description": "YouTube channel handle to audit",
                "x-snappy-position": 0
              }
            },
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            },
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many recent videos to audit, newest first. The count is the FLAG --limit, never a positional word; default 20, ceiling 50",
                "default": 20,
                "minimum": 1,
                "maximum": 50,
                "x-snappy-flag": "--limit"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/brief": {
        "get": {
          "operationId": "snappy_thumbnails_brief",
          "summary": "snappy-thumbnails brief",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "title",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Human-readable title",
                "x-snappy-position": 0
              }
            },
            {
              "name": "hook",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The hook line the thumbnail must carry",
                "x-snappy-position": 1
              }
            },
            {
              "name": "style",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--style"
              }
            },
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/generate": {
        "post": {
          "operationId": "snappy_thumbnails_generate",
          "summary": "snappy-thumbnails generate",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "brief-file": {
                      "type": "string",
                      "description": "Path to the JSON brief written by `brief`",
                      "x-snappy-position": 0
                    },
                    "count": {
                      "type": "string",
                      "description": "How many to generate",
                      "x-snappy-position": 1
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "brief-file"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/set": {
        "post": {
          "operationId": "snappy_thumbnails_set",
          "summary": "snappy-thumbnails set",
          "x-snappy-effect": "write",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "video-id": {
                      "type": "string",
                      "description": "YouTube video id",
                      "x-snappy-position": 0
                    },
                    "file": {
                      "type": "string",
                      "description": "Path to the file",
                      "x-snappy-position": 1
                    }
                  },
                  "required": [
                    "video-id",
                    "file"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/variants": {
        "get": {
          "operationId": "snappy_thumbnails_variants",
          "summary": "snappy-thumbnails variants",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "slug",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Video slug the variants are generated for",
                "x-snappy-position": 0
              }
            },
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      }
    },
    "components": {
      "schemas": {
        "SnappyRefusal": {
          "type": "object",
          "required": [
            "outcome",
            "code",
            "message"
          ],
          "properties": {
            "outcome": {
              "type": "string",
              "const": "refused"
            },
            "code": {
              "type": "string",
              "description": "A row of the closed refusal table."
            },
            "message": {
              "type": "string"
            },
            "fix": {
              "type": "string"
            }
          }
        }
      }
    },
    "x-snappy-requires": [
      "GEMINI_API_KEY",
      "YOUTUBE_CLIENT_ID",
      "YOUTUBE_CLIENT_SECRET"
    ]
  }
}