{
  "schema": "snappy-card/1",
  "skill": "snappy-youtube",
  "tier": "public",
  "origin_tier": "base",
  "purpose": "You are operating as the organic YouTube channel for Snappy (@robert.boulos, 306 subs, dormant since Oct 2025). This file is the operational contract. The full SKILL.md exists for reference, but the rules below are load-bearing -- if you deviate, the video is wrong.",
  "verbs": [
    {
      "name": "auth",
      "args": [],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts auth"
    },
    {
      "name": "comments",
      "args": [
        "video-id"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts comments <video-id>"
    },
    {
      "name": "thread",
      "args": [
        "video-id"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts thread <video-id>"
    },
    {
      "name": "channel",
      "args": [
        "handle?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts channel"
    },
    {
      "name": "reply",
      "args": [
        "comment-id",
        "text"
      ],
      "effect": "send",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts reply <comment-id> \"<text>\""
    },
    {
      "name": "recent",
      "args": [
        "channel?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts recent"
    },
    {
      "name": "search",
      "args": [
        "query"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts search \"<query>\""
    },
    {
      "name": "token-status",
      "args": [],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts token-status"
    },
    {
      "name": "thumbnail-set",
      "args": [
        "video-id",
        "file-path"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts thumbnail-set <video-id> <file-path>"
    },
    {
      "name": "upload",
      "args": [
        "file-path",
        "title",
        "description"
      ],
      "effect": "post",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts upload <file-path> \"<title>\" <description>"
    },
    {
      "name": "video",
      "args": [
        "video-id"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-youtube/api.ts video <video-id>"
    }
  ],
  "faces": [],
  "agents": [],
  "uses": [],
  "used_by": [
    "snappy-ads",
    "snappy-ffmpeg",
    "snappy-image",
    "snappy-remotion"
  ],
  "checks": {
    "pass": 47,
    "fail": 7,
    "defer": 7,
    "line": "47 of 54 checks pass",
    "failing": [
      {
        "id": 9,
        "rule": "Time-sensitive prose is isolated under Old patterns",
        "says": "2 time-sensitive instruction line(s) outside Old patterns"
      },
      {
        "id": 10,
        "rule": "One term names each concept",
        "says": "mixed synonym sets: endpoint/url/route/path"
      },
      {
        "id": 40,
        "rule": "Metadata, skill body, and resources follow three-level budgets",
        "says": "metadata ~165 tokens; body ~4202 tokens"
      },
      {
        "id": 43,
        "rule": "A read is one call, not a queued job",
        "says": "auth:p50>3s-or-missing"
      },
      {
        "id": 54,
        "rule": "At least three eval scenarios precede ready status",
        "says": "0 eval scenario(s); ready=true"
      },
      {
        "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",
    "analytics.md",
    "api.test.ts",
    "api.ts",
    "comment-road.ts",
    "content-calendar.md",
    "face.test.ts",
    "faces/components/youtube-faces.css",
    "faces/components/youtube-faces.tsx",
    "faces/family.tsx",
    "faces/fixtures/youtube-channel.json",
    "faces/fixtures/youtube-comments.json",
    "faces/fixtures/youtube-package.json",
    "faces/fixtures/youtube-reply.json",
    "faces/fixtures/youtube-video.json",
    "production-workflow.md",
    "refusals.test.ts",
    "repurposing.md",
    "stage.test.ts",
    "youtube-wire.ts"
  ],
  "total_size": 216417,
  "checksum": "sha256:3baedc171ad107926ea00060bc101af9b88d0156daa0f3aedebb64f07087cab1",
  "built_at": "2026-09-09T22:11:35.324Z",
  "openapi": {
    "openapi": "3.1.0",
    "info": {
      "title": "snappy-youtube",
      "version": "0.0.0",
      "description": "Organic YouTube content creation and channel management for Snappy. Channel strategy, video production workflow, upload via Xano API, title/thumbnail formulas, SEO checklist, weekly cadence, repurposing into 5 channels (clips, blog, email, LinkedIn, Skool), YouTube Studio scraping for analytics, performance benchmarks, and optimization triggers. Triggers on: youtube, youtube channel, youtube video, video strategy, youtube upload, channel management, video content, youtube seo, thumbnail, video script, youtube shorts, content calendar youtube, video repurpose, youtube analytics, youtube studio, ctr, avd, organic video, long-form video, video for snappy."
    },
    "x-snappy-transport": "cli",
    "servers": [
      {
        "url": "npx tsx ~/.claude/skills/snappy-youtube/api.ts",
        "description": "The hand itself. Every path is a verb word, every query or body key an argument."
      }
    ],
    "paths": {
      "/auth": {
        "get": {
          "operationId": "snappy_youtube_auth",
          "summary": "snappy-youtube auth",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/comments": {
        "get": {
          "operationId": "snappy_youtube_comments",
          "summary": "snappy-youtube comments",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "video-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The YouTube video id — the 11 characters after `watch?v=`",
                "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`."
            }
          }
        }
      },
      "/thread": {
        "get": {
          "operationId": "snappy_youtube_thread",
          "summary": "snappy-youtube thread",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "video-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The YouTube video id — the 11 characters after `watch?v=`",
                "x-snappy-position": 0
              }
            },
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many comments of that conversation to return. The count is the FLAG --limit, never a positional word; default 20, ceiling 100",
                "default": 20,
                "minimum": 1,
                "maximum": 100,
                "x-snappy-flag": "--limit"
              }
            },
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/channel": {
        "get": {
          "operationId": "snappy_youtube_channel",
          "summary": "snappy-youtube channel",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "handle",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string",
                "description": "The channel's @handle; omit for the authenticated account's own channel",
                "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`."
            }
          }
        }
      },
      "/reply": {
        "post": {
          "operationId": "snappy_youtube_reply",
          "summary": "snappy-youtube reply",
          "x-snappy-effect": "send",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": true,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "comment-id": {
                      "type": "string",
                      "description": "The comment id being replied to, from a `comments` row's `id`",
                      "x-snappy-position": 0
                    },
                    "text": {
                      "type": "string",
                      "description": "The reply's words, verbatim",
                      "x-snappy-position": 1
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "comment-id",
                    "text"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/recent": {
        "get": {
          "operationId": "snappy_youtube_recent",
          "summary": "snappy-youtube recent",
          "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": "The channel's @handle, or `mine` for the authenticated account's own",
                "default": "mine",
                "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 videos to return; the count is the FLAG --limit, never a positional word",
                "default": 5,
                "maximum": 50,
                "x-snappy-flag": "--limit"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/search": {
        "get": {
          "operationId": "snappy_youtube_search",
          "summary": "snappy-youtube search",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "query",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Search text matched against video titles and descriptions",
                "x-snappy-position": 0
              }
            },
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many results to return. 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`."
            }
          }
        }
      },
      "/token-status": {
        "get": {
          "operationId": "snappy_youtube_token_status",
          "summary": "snappy-youtube token-status",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/thumbnail-set": {
        "post": {
          "operationId": "snappy_youtube_thumbnail_set",
          "summary": "snappy-youtube thumbnail-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": "The YouTube video id — the 11 characters after `watch?v=`",
                      "x-snappy-position": 0
                    },
                    "file-path": {
                      "type": "string",
                      "description": "Path to the thumbnail image on this Computer; YouTube refuses anything at or over 2 MB",
                      "x-snappy-position": 1
                    }
                  },
                  "required": [
                    "video-id",
                    "file-path"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/upload": {
        "post": {
          "operationId": "snappy_youtube_upload",
          "summary": "snappy-youtube upload",
          "x-snappy-effect": "post",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": true,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file-path": {
                      "type": "string",
                      "description": "Path to the video file on this Computer",
                      "x-snappy-position": 0
                    },
                    "title": {
                      "type": "string",
                      "description": "The video's title, verbatim",
                      "x-snappy-position": 1
                    },
                    "description": {
                      "type": "string",
                      "description": "The video's description, verbatim",
                      "x-snappy-position": 2
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "file-path",
                    "title",
                    "description"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/video": {
        "get": {
          "operationId": "snappy_youtube_video",
          "summary": "snappy-youtube video",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "video-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The YouTube video id — the 11 characters after `watch?v=`",
                "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"
    ]
  }
}