{
  "schema": "snappy-card/1",
  "skill": "snappy-ffmpeg",
  "tier": "public",
  "origin_tier": "base",
  "purpose": "You are operating the local ffmpeg primitive layer. All operations run on this Mac using `/opt/homebrew/bin/ffmpeg` (v8.1). This skill does NOT SSH anywhere -- that is `snappy-video`'s job. This skill is the low-level building block that `snappy-video`, `snappy-image`, `snappy-post`, and other skills call when they need to manipulate media files locally.",
  "verbs": [
    {
      "name": "burn-subs",
      "args": [
        "path",
        "subs-path"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts burn-subs <path> <subs-path>"
    },
    {
      "name": "compress",
      "args": [
        "path"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts compress <path>"
    },
    {
      "name": "concat",
      "args": [
        "path-1",
        "path-2"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts concat <path-1> <path-2>"
    },
    {
      "name": "extract-audio",
      "args": [
        "path"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts extract-audio <path>"
    },
    {
      "name": "loudnorm",
      "args": [
        "path"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts loudnorm <path>"
    },
    {
      "name": "overlay",
      "args": [
        "base",
        "overlay",
        "position?"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts overlay <base> <overlay>"
    },
    {
      "name": "probe",
      "args": [
        "path"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts probe <path>"
    },
    {
      "name": "resize",
      "args": [
        "path"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts resize <path>"
    },
    {
      "name": "thumbnail",
      "args": [
        "path",
        "timestamp?"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts thumbnail <path>"
    },
    {
      "name": "trim",
      "args": [
        "path",
        "start",
        "end"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts trim <path> <start> <end>"
    }
  ],
  "faces": [],
  "agents": [],
  "uses": [
    "snappy-video",
    "snappy-image",
    "snappy-post",
    "snappy-youtube",
    "snappy-settings",
    "snappy-faces"
  ],
  "used_by": [
    "snappy-cleanshot"
  ],
  "checks": {
    "pass": 31,
    "fail": 3,
    "defer": 27,
    "line": "31 of 34 checks pass",
    "failing": [
      {
        "id": 7,
        "rule": "Promised and declared verbs have no set difference",
        "says": "promised-only=[help]; loader-missing=[]"
      },
      {
        "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.ts",
    "contract.test.ts",
    "media-face.test.ts"
  ],
  "total_size": 50631,
  "checksum": "sha256:8a648bb0a8a51601210ea0c45b0d227cb94ebf200e6bed870733cfc1b2ff6219",
  "built_at": "2026-09-09T22:09:16.030Z",
  "openapi": {
    "openapi": "3.1.0",
    "info": {
      "title": "snappy-ffmpeg",
      "version": "0.0.0",
      "description": "Local ffmpeg primitive layer for media manipulation. Probe, trim, concat, resize, compress, extract audio, burn subtitles, thumbnail, loudness normalize, overlay. Runs locally on this Mac (NOT via SSH to Mac Mini — that is snappy-video). Building block called by snappy-remotion, snappy-content, snappy-video for local ops. Triggers on: ffmpeg local, probe video, trim clip, concat videos, resize video, compress video local, extract audio local, burn subs local, thumbnail local, loudnorm, overlay, pip, watermark, ffprobe."
    },
    "x-snappy-transport": "cli",
    "servers": [
      {
        "url": "npx tsx ~/.claude/skills/snappy-ffmpeg/api.ts",
        "description": "The hand itself. Every path is a verb word, every query or body key an argument."
      }
    ],
    "paths": {
      "/burn-subs": {
        "post": {
          "operationId": "snappy_ffmpeg_burn_subs",
          "summary": "snappy-ffmpeg burn-subs",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Path of the media file the verb reads",
                      "x-snappy-position": 0
                    },
                    "subs-path": {
                      "type": "string",
                      "description": "Subtitle file burned into the picture",
                      "x-snappy-position": 1
                    }
                  },
                  "required": [
                    "path",
                    "subs-path"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/compress": {
        "post": {
          "operationId": "snappy_ffmpeg_compress",
          "summary": "snappy-ffmpeg compress",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Path of the media file the verb reads",
                      "x-snappy-position": 0
                    }
                  },
                  "required": [
                    "path"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/concat": {
        "post": {
          "operationId": "snappy_ffmpeg_concat",
          "summary": "snappy-ffmpeg concat",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path-1": {
                      "type": "string",
                      "description": "First clip in the join",
                      "x-snappy-position": 0
                    },
                    "path-2": {
                      "type": "string",
                      "description": "Second clip in the join",
                      "x-snappy-position": 1
                    }
                  },
                  "required": [
                    "path-1",
                    "path-2"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/extract-audio": {
        "post": {
          "operationId": "snappy_ffmpeg_extract_audio",
          "summary": "snappy-ffmpeg extract-audio",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Path of the media file the verb reads",
                      "x-snappy-position": 0
                    }
                  },
                  "required": [
                    "path"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/loudnorm": {
        "post": {
          "operationId": "snappy_ffmpeg_loudnorm",
          "summary": "snappy-ffmpeg loudnorm",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Path of the media file the verb reads",
                      "x-snappy-position": 0
                    }
                  },
                  "required": [
                    "path"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/overlay": {
        "post": {
          "operationId": "snappy_ffmpeg_overlay",
          "summary": "snappy-ffmpeg overlay",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "base": {
                      "type": "string",
                      "description": "Clip the overlay is drawn onto",
                      "x-snappy-position": 0
                    },
                    "overlay": {
                      "type": "string",
                      "description": "Image or clip drawn on top",
                      "x-snappy-position": 1
                    },
                    "position": {
                      "type": "string",
                      "description": "Corner the overlay sits in",
                      "enum": [
                        "tl",
                        "tr",
                        "bl",
                        "br",
                        "center"
                      ],
                      "x-snappy-position": 2
                    }
                  },
                  "required": [
                    "base",
                    "overlay"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/probe": {
        "get": {
          "operationId": "snappy_ffmpeg_probe",
          "summary": "snappy-ffmpeg probe",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "parameters": [
            {
              "name": "path",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Path of the media file the verb reads",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/resize": {
        "post": {
          "operationId": "snappy_ffmpeg_resize",
          "summary": "snappy-ffmpeg resize",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Path of the media file the verb reads",
                      "x-snappy-position": 0
                    }
                  },
                  "required": [
                    "path"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/thumbnail": {
        "post": {
          "operationId": "snappy_ffmpeg_thumbnail",
          "summary": "snappy-ffmpeg thumbnail",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Path of the media file the verb reads",
                      "x-snappy-position": 0
                    },
                    "timestamp": {
                      "type": "string",
                      "description": "Position the still is taken at, as HH:MM:SS",
                      "x-snappy-position": 1
                    }
                  },
                  "required": [
                    "path"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/trim": {
        "post": {
          "operationId": "snappy_ffmpeg_trim",
          "summary": "snappy-ffmpeg trim",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": false
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "Path of the media file the verb reads",
                      "x-snappy-position": 0
                    },
                    "start": {
                      "type": "string",
                      "description": "Trim start as HH:MM:SS",
                      "x-snappy-position": 1
                    },
                    "end": {
                      "type": "string",
                      "description": "Trim end as HH:MM:SS",
                      "x-snappy-position": 2
                    }
                  },
                  "required": [
                    "path",
                    "start",
                    "end"
                  ]
                }
              }
            }
          },
          "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"
            }
          }
        }
      }
    }
  }
}