{
  "schema": "snappy-card/1",
  "skill": "snappy-faces",
  "tier": "public",
  "origin_tier": "base",
  "purpose": "Use this skill whenever work should be shown as the thing it is instead of described in markdown. `draw` produces a PNG for picture-capable channels, `lang` emits the live OpenUI program for MCP Apps, and `faces` lists the canonical manifest families and slots.",
  "verbs": [
    {
      "name": "draw",
      "args": [
        "kind"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-faces/api.ts draw <kind>"
    },
    {
      "name": "lang",
      "args": [
        "kind"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-faces/api.ts lang <kind>"
    },
    {
      "name": "faces",
      "args": [],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-faces/api.ts faces"
    },
    {
      "name": "grammar",
      "args": [],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-faces/api.ts grammar"
    },
    {
      "name": "sheet",
      "args": [],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-faces/api.ts sheet"
    }
  ],
  "faces": [],
  "agents": [],
  "uses": [],
  "used_by": [
    "snappy-ax",
    "snappy-cleanshot",
    "snappy-ffmpeg",
    "snappy-image",
    "snappy-os-operator",
    "snappy-remotion",
    "snappy-runner",
    "snappy-voice-control"
  ],
  "checks": {
    "pass": 35,
    "fail": 14,
    "defer": 12,
    "line": "35 of 49 checks pass",
    "failing": [
      {
        "id": 4,
        "rule": "Near-neighbour descriptions cross-reference each other",
        "says": "near neighbours not named: snappy-infra, snappy-linkedin, snappy-telegram, snappy-watchtower, snappy-xano-mcp"
      },
      {
        "id": 8,
        "rule": "Skill body and reference graph stay within disclosure budgets",
        "says": "482 lines; ~7883 tokens"
      },
      {
        "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, field/box/element"
      },
      {
        "id": 11,
        "rule": "Each task presents one default, not a menu",
        "says": "2 line(s) present three-way alternative menus"
      },
      {
        "id": 13,
        "rule": "Every argument is described and unambiguous",
        "says": "draw.kind missing description; lang.kind missing description"
      },
      {
        "id": 18,
        "rule": "Every verb declares a side-effect class",
        "says": "missing class: draw, lang, faces, grammar, sheet"
      },
      {
        "id": 19,
        "rule": "Published annotations are derived from the effect class",
        "says": "annotation defaults mismatch: draw, lang, faces, grammar, sheet"
      },
      {
        "id": 23,
        "rule": "Responses obey hard and soft caps",
        "says": "0 hard-cap breach(es); 1 unannounced default-read soft-cap breach(es)"
      },
      {
        "id": 33,
        "rule": "Refusal codes form one tested closed table",
        "says": "HAND_CONTRACT.refusals missing"
      },
      {
        "id": 40,
        "rule": "Metadata, skill body, and resources follow three-level budgets",
        "says": "metadata ~99 tokens; body ~7883 tokens"
      },
      {
        "id": 45,
        "rule": "Long work is a separately named job verb",
        "says": "3 read(s) not execution=call; queue implementation=false"
      },
      {
        "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",
    "build.mjs",
    "compose.ts",
    "dist/build-report.json",
    "dist/snappy-faces-agent.js",
    "dist/snappy-faces-blocks.js",
    "dist/snappy-faces-calendar.js",
    "dist/snappy-faces-code.js",
    "dist/snappy-faces-data.js",
    "dist/snappy-faces-deploy.js",
    "dist/snappy-faces-doc.js",
    "dist/snappy-faces-freshbooks.js",
    "dist/snappy-faces-github.js",
    "dist/snappy-faces-gmail.js",
    "dist/snappy-faces-imageset.js",
    "dist/snappy-faces-imessage.js",
    "dist/snappy-faces-instagram.js",
    "dist/snappy-faces-knowledge.js",
    "dist/snappy-faces-krisp.js",
    "dist/snappy-faces-linkedin.js",
    "dist/snappy-faces-media.js",
    "dist/snappy-faces-notion.js",
    "dist/snappy-faces-schedule.js",
    "dist/snappy-faces-sheets.js",
    "dist/snappy-faces-skool.js",
    "dist/snappy-faces-slack.js",
    "dist/snappy-faces-statechange.js",
    "dist/snappy-faces-stripe.js",
    "dist/snappy-faces-telegram.js",
    "dist/snappy-faces-thumbnails.js",
    "dist/snappy-faces-transcripts.js",
    "dist/snappy-faces-walkthrough.js",
    "dist/snappy-faces-whatsapp.js",
    "dist/snappy-faces-widget.css",
    "dist/snappy-faces-widget.js",
    "dist/snappy-faces-x.js",
    "dist/snappy-faces-youtube.js",
    "face-data.ts",
    "face-family.ts",
    "face-homes.ts",
    "faces-wired.ts",
    "families/agent.tsx",
    "families/blocks.tsx",
    "families/code.tsx",
    "families/data.tsx",
    "families/doc.tsx",
    "families/imageset.tsx",
    "families/instagram.tsx",
    "families/media.tsx",
    "families/schedule.tsx",
    "families/sheets.tsx",
    "families/stripe.tsx",
    "families/x.tsx",
    "family-loader.ts",
    "fixtures/agent-permission.json",
    "fixtures/agent-run.json",
    "fixtures/blocks-audio.json",
    "fixtures/blocks-options.json",
    "fixtures/blocks-repair.json",
    "fixtures/code-diff.json",
    "fixtures/data-chart.json",
    "fixtures/data-table.json",
    "fixtures/doc-card.json",
    "fixtures/imageset-grid.json",
    "fixtures/instagram-carousel.json",
    "fixtures/instagram-grid.json",
    "fixtures/instagram-post.json",
    "fixtures/instagram-reel.json",
    "fixtures/media-document.json",
    "fixtures/media-google-doc.json",
    "fixtures/media-html.json",
    "fixtures/media-image.json",
    "fixtures/media-pdf.json",
    "fixtures/media-slides.json",
    "fixtures/media-video.json",
    "fixtures/schedule-pick.json",
    "fixtures/sheets-rows.json",
    "fixtures/stripe-payment.json",
    "fixtures/x-decision.json",
    "fixtures/x-post.json",
    "fixtures/x-thread.json",
    "host-test.html",
    "lang-params.generated.ts",
    "library/app-shell-genui.css",
    "library/src/asset-base.ts",
    "library/src/components/avatar-color.ts",
    "library/src/components/block-faces.css",
    "library/src/components/block-faces.tsx",
    "library/src/components/chat-compose.css",
    "library/src/components/chat-compose.tsx",
    "library/src/components/chat-decision.css",
    "library/src/components/chat-decision.tsx",
    "library/src/components/count-words.ts",
    "library/src/components/decision-shell.tsx",
    "library/src/components/destination-previews.css",
    "library/src/components/doc-card.css",
    "library/src/components/doc-card.tsx",
    "library/src/components/document-view.css",
    "library/src/components/document-view.tsx",
    "library/src/components/domain-logos.tsx",
    "library/src/components/door.ts",
    "library/src/components/email-body-text.ts",
    "library/src/components/email-preview.tsx",
    "library/src/components/face-decision.ts",
    "library/src/components/face-edit.css",
    "library/src/components/face-edit.tsx",
    "library/src/components/face-host-nodes.tsx",
    "library/src/components/face-host-surface.ts",
    "library/src/components/face-host-words.ts",
    "library/src/components/face-tool-call.ts",
    "library/src/components/field-text.ts",
    "library/src/components/google-doc-face.css",
    "library/src/components/google-doc-face.tsx",
    "library/src/components/html-preview-scene.ts",
    "library/src/components/html-preview.tsx",
    "library/src/components/image-set-faces.css",
    "library/src/components/image-set-faces.tsx",
    "library/src/components/imessage-chat.css",
    "library/src/components/instagram-faces.css",
    "library/src/components/instagram-faces.tsx",
    "library/src/components/loading-honesty.ts",
    "library/src/components/made-thing-previews.css",
    "library/src/components/made-thing-previews.tsx",
    "library/src/components/managed-from.css",
    "library/src/components/managed-from.tsx",
    "library/src/components/markdown.tsx",
    "library/src/components/operator-identity-context.ts",
    "library/src/components/payment-preview.tsx",
    "library/src/components/person.css",
    "library/src/components/person.tsx",
    "library/src/components/post-media.css",
    "library/src/components/post-media.tsx",
    "library/src/components/query-row-utils.tsx",
    "library/src/components/row-press.css",
    "library/src/components/row-press.tsx",
    "library/src/components/sender-avatar.ts",
    "library/src/components/sheet-rows-preview.tsx",
    "library/src/components/slack-message-list.css",
    "library/src/components/slack-message-list.tsx",
    "library/src/components/slack-message-preview.css",
    "library/src/components/slack-message-preview.tsx",
    "library/src/components/social-card-format.ts",
    "library/src/components/social-decision.tsx",
    "library/src/components/telegram-message-preview.tsx",
    "library/src/components/video-preview.tsx",
    "library/src/components/whatsapp-chat.css",
    "library/src/components/work-output-faces.css",
    "library/src/components/work-output-faces.tsx",
    "library/src/components/written-blocks.ts",
    "library/src/components/x-decision.tsx",
    "library/src/components/x-face.css",
    "library/src/components/x-post.tsx",
    "library/src/freshbooks-money.ts",
    "library/src/host-routes.ts",
    "library/src/manifest.ts",
    "measure-renderers.mjs",
    "package-lock.json",
    "package.json",
    "proof-report.json",
    "row-reads.ts",
    "shots/ALL-FACES.png",
    "shots/EDIT-IN-PLACE.png",
    "shots/NEW-FACES-20260909.png",
    "shots/NEW-FACES-PHONE-20260909.png",
    "shots/agent-permission.png",
    "shots/agent-run.png",
    "shots/blocks-audio.png",
    "shots/blocks-options.png",
    "shots/blocks-repair.png",
    "shots/calendar-event.png",
    "shots/calendar-week.png",
    "shots/code-diff.png",
    "shots/data-chart.png",
    "shots/data-table.png",
    "shots/deploy-receipt.png",
    "shots/doc-card.png",
    "shots/edit/agent-permission.png",
    "shots/edit/blocks-options.png",
    "shots/edit/doc-card.png",
    "shots/edit/freshbooks-decision.png",
    "shots/edit/github-comment.png",
    "shots/edit/gmail-bulk-change.png",
    "shots/edit/gmail-decision.png",
    "shots/edit/gmail-message.png",
    "shots/edit/gmail-reply.png",
    "shots/edit/gmail-thread.png",
    "shots/edit/imageset-grid.png",
    "shots/edit/imessage-decision.png",
    "shots/edit/linkedin-decision.png",
    "shots/edit/linkedin-post.png",
    "shots/edit/notion-append.png",
    "shots/edit/proofs.json",
    "shots/edit/schedule-pick.png",
    "shots/edit/skool-comment.png",
    "shots/edit/slack-decision.png",
    "shots/edit/statechange-decision.png",
    "shots/edit/statechange-message.png",
    "shots/edit/telegram-decision.png",
    "shots/edit/thumbnails-set.png",
    "shots/edit/whatsapp-decision.png",
    "shots/edit/x-decision.png",
    "shots/edit/youtube-reply.png",
    "shots/freshbooks-decision.png",
    "shots/freshbooks-invoice.png",
    "shots/freshbooks-list.png",
    "shots/github-comment.png",
    "shots/github-comments.png",
    "shots/github-pr.png",
    "shots/gmail-bulk-change.png",
    "shots/gmail-decision.png",
    "shots/gmail-draft.png",
    "shots/gmail-list.png",
    "shots/gmail-message.png",
    "shots/gmail-reply.png",
    "shots/gmail-thread.png",
    "shots/imageset-grid.png",
    "shots/imessage-compose.png",
    "shots/imessage-decision.png",
    "shots/imessage-message.png",
    "shots/imessage-thread.png",
    "shots/instagram-carousel.png",
    "shots/instagram-grid.png",
    "shots/instagram-post.png",
    "shots/instagram-reel.png",
    "shots/knowledge-company.png",
    "shots/knowledge-contact.png",
    "shots/knowledge-hits.png",
    "shots/krisp-actions.png",
    "shots/krisp-hits.png",
    "shots/krisp-meeting.png",
    "shots/linkedin-comments.png",
    "shots/linkedin-decision.png",
    "shots/linkedin-feed.png",
    "shots/linkedin-post-published.png",
    "shots/linkedin-post.png",
    "shots/linkedin-profile.png",
    "shots/media-document.png",
    "shots/media-google-doc.png",
    "shots/media-html.png",
    "shots/media-image.png",
    "shots/media-pdf.png",
    "shots/media-slides.png",
    "shots/media-video.png",
    "shots/notion-append.png",
    "shots/notion-blocks.png",
    "shots/notion-page.png",
    "shots/notion-pages.png",
    "shots/paint/paints.json",
    "shots/phone/data-table.png",
    "shots/phone/doc-card.png",
    "shots/phone/freshbooks-decision.png",
    "shots/phone/github-comment.png",
    "shots/phone/github-comments.png",
    "shots/phone/imageset-grid.png",
    "shots/phone/knowledge-contact.png",
    "shots/phone/knowledge-hits.png",
    "shots/phone/krisp-hits.png",
    "shots/phone/krisp-meeting.png",
    "shots/phone/notion-append.png",
    "shots/phone/notion-blocks.png",
    "shots/phone/statechange-decision.png",
    "shots/phone/statechange-feed.png",
    "shots/phone/statechange-message.png",
    "shots/phone/thumbnails-set.png",
    "shots/phone/transcripts-hits.png",
    "shots/phone/walkthrough-steps.png",
    "shots/phone/youtube-reply.png",
    "shots/schedule-pick.png",
    "shots/sheets-rows.png",
    "shots/skool-classroom.png",
    "shots/skool-comments.png",
    "shots/skool-feed.png",
    "shots/skool-post.png",
    "shots/slack-channels.png",
    "shots/slack-decision.png",
    "shots/slack-draft.png",
    "shots/slack-list.png",
    "shots/slack-message.png",
    "shots/slack-thread.png",
    "shots/statechange-decision.png",
    "shots/statechange-feed.png",
    "shots/statechange-message.png",
    "shots/statechange-post.png",
    "shots/statechange-thread.png",
    "shots/statechange-unread.png",
    "shots/stripe-payment.png",
    "shots/telegram-decision.png",
    "shots/telegram-draft.png",
    "shots/telegram-list.png",
    "shots/telegram-message.png",
    "shots/thumbnails-set.png",
    "shots/transcripts-hits.png",
    "shots/transcripts-nugget.png",
    "shots/walkthrough-steps.png",
    "shots/whatsapp-compose.png",
    "shots/whatsapp-decision.png",
    "shots/whatsapp-list.png",
    "shots/whatsapp-message.png",
    "shots/whatsapp-thread.png",
    "shots/x-decision.png",
    "shots/x-post.png",
    "shots/x-thread.png",
    "shots/youtube-channel.png",
    "shots/youtube-comments.png",
    "shots/youtube-package.png",
    "shots/youtube-reply.png",
    "shots/youtube-video.png",
    "tests/api.test.ts",
    "tests/budget.test.ts",
    "tests/compose.test.ts",
    "tests/edit-proof.d.mts",
    "tests/edit-proof.mjs",
    "tests/edit-proof.test.ts",
    "tests/identity.test.ts",
    "tests/lang-json.test.ts",
    "tests/no-any.test.ts",
    "tests/paint-proof.d.mts",
    "tests/paint-proof.mjs",
    "tests/paint-proof.test.ts",
    "tests/wiring.test.ts",
    "tsconfig.json",
    "types/standalone-markdown.d.ts",
    "widget-entry.tsx",
    "widget.css"
  ],
  "total_size": 16164912,
  "checksum": "sha256:fac9811f558e863b60df3a7280de83c900b1aefde0caee08f7e76d67c8832c49",
  "built_at": "2026-09-09T22:09:02.832Z",
  "openapi": {
    "openapi": "3.1.0",
    "info": {
      "title": "snappy-faces",
      "version": "0.0.0",
      "description": "Draw Snappy work objects as their channel-faithful UI faces. Triggers on: an agent made an email, inbox list, LinkedIn post, document, media object, or other typed result that should be shown visually; draw/render/show the card; make a PNG for Telegram/iMessage/email; emit OpenUI Lang for MCP Apps or Claude Desktop; inspect face families/slots; build the shared faces widget; ui://snappy/faces."
    },
    "x-snappy-transport": "cli",
    "servers": [
      {
        "url": "npx tsx ~/.claude/skills/snappy-faces/api.ts",
        "description": "The hand itself. Every path is a verb word, every query or body key an argument."
      }
    ],
    "paths": {
      "/draw": {
        "post": {
          "operationId": "snappy_faces_draw",
          "summary": "snappy-faces draw",
          "x-snappy-effect": "write-reversible",
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "type": "string",
                      "x-snappy-position": 0
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    },
                    "data-file": {
                      "x-snappy-flag": "--data-file"
                    },
                    "data-json": {
                      "x-snappy-flag": "--data-json"
                    },
                    "out": {
                      "x-snappy-flag": "--out"
                    },
                    "program": {
                      "x-snappy-flag": "--program"
                    },
                    "program-file": {
                      "x-snappy-flag": "--program-file"
                    },
                    "width": {
                      "x-snappy-flag": "--width"
                    },
                    "height": {
                      "x-snappy-flag": "--height"
                    },
                    "scale": {
                      "x-snappy-flag": "--scale"
                    }
                  },
                  "required": [
                    "kind"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/lang": {
        "get": {
          "operationId": "snappy_faces_lang",
          "summary": "snappy-faces lang",
          "x-snappy-effect": "read",
          "parameters": [
            {
              "name": "kind",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "x-snappy-position": 0
              }
            },
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            },
            {
              "name": "data-file",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--data-file"
              }
            },
            {
              "name": "data-json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--data-json"
              }
            },
            {
              "name": "program",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--program"
              }
            },
            {
              "name": "program-file",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--program-file"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/faces": {
        "get": {
          "operationId": "snappy_faces_faces",
          "summary": "snappy-faces faces",
          "x-snappy-effect": "read",
          "parameters": [
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/grammar": {
        "get": {
          "operationId": "snappy_faces_grammar",
          "summary": "snappy-faces grammar",
          "x-snappy-effect": "read",
          "parameters": [
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/sheet": {
        "post": {
          "operationId": "snappy_faces_sheet",
          "summary": "snappy-faces sheet",
          "x-snappy-effect": "write-reversible",
          "requestBody": {
            "required": false,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "json": {
                      "x-snappy-flag": "--json"
                    },
                    "out": {
                      "x-snappy-flag": "--out"
                    }
                  }
                }
              }
            }
          },
          "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"
            }
          }
        }
      }
    }
  }
}