{
  "schema": "snappy-card/1",
  "skill": "snappy-gmail",
  "tier": "public",
  "origin_tier": "base",
  "purpose": "Rules:",
  "verbs": [
    {
      "name": "list",
      "args": [
        "limit?",
        "query?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-gmail/api.ts list"
    },
    {
      "name": "get",
      "args": [
        "message-id",
        "format?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-gmail/api.ts get <message-id>"
    },
    {
      "name": "thread",
      "args": [
        "thread-id",
        "source?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-gmail/api.ts thread <thread-id>"
    },
    {
      "name": "draft",
      "args": [
        "to",
        "subject",
        "text",
        "thread-id?"
      ],
      "effect": "draft",
      "firstCall": "npx tsx ~/.claude/skills/snappy-gmail/api.ts draft <to> \"<subject>\" \"<text>\""
    },
    {
      "name": "reply",
      "args": [
        "thread-id",
        "text"
      ],
      "effect": "send",
      "firstCall": "npx tsx ~/.claude/skills/snappy-gmail/api.ts reply <thread-id> \"<text>\""
    },
    {
      "name": "send",
      "args": [
        "to",
        "subject",
        "text"
      ],
      "effect": "send",
      "firstCall": "npx tsx ~/.claude/skills/snappy-gmail/api.ts send <to> \"<subject>\" \"<text>\""
    },
    {
      "name": "archive",
      "args": [
        "ids"
      ],
      "effect": "write",
      "firstCall": "npx tsx ~/.claude/skills/snappy-gmail/api.ts archive '[\"<ids>\"]'"
    }
  ],
  "faces": [],
  "agents": [],
  "uses": [],
  "used_by": [],
  "checks": {
    "pass": 49,
    "fail": 6,
    "defer": 6,
    "line": "49 of 55 checks pass",
    "failing": [
      {
        "id": 4,
        "rule": "Near-neighbour descriptions cross-reference each other",
        "says": "near neighbours not named: snappy-runner, snappy-specwatch"
      },
      {
        "id": 9,
        "rule": "Time-sensitive prose is isolated under Old patterns",
        "says": "1 time-sensitive instruction line(s) outside Old patterns"
      },
      {
        "id": 40,
        "rule": "Metadata, skill body, and resources follow three-level budgets",
        "says": "metadata ~168 tokens; body ~2531 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)"
      },
      {
        "id": 61,
        "rule": "The vendor's own spec still has what this hand offers, and this hand offers what the vendor added",
        "says": "Google added format raw — this hand still offers full, metadata, minimal · Google moved from 20260907 to 20260903 since 2026-09-09T20:44:11Z"
      }
    ]
  },
  "coverage": "covers 7 of 79 operations of Google, checked 2026-09-09",
  "spec": {
    "kind": "discovery",
    "vendor": "Google",
    "version": "20260903",
    "url": "https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest",
    "checked": "2026-09-09",
    "covers": 7,
    "of": 79
  },
  "evals": null,
  "files": [
    "AGENTS.md",
    "SKILL.md",
    "api.ts",
    "face.test.ts",
    "faces/components/gmail-bulk.css",
    "faces/components/gmail-bulk.tsx",
    "faces/components/gmail-decision.css",
    "faces/components/gmail-decision.tsx",
    "faces/components/gmail-face.css",
    "faces/components/gmail-face.tsx",
    "faces/components/gmail-inbox-list.css",
    "faces/components/gmail-inbox-list.tsx",
    "faces/components/gmail-message.css",
    "faces/components/gmail-message.tsx",
    "faces/components/gmail-reply.css",
    "faces/components/gmail-reply.tsx",
    "faces/components/gmail-thread.css",
    "faces/components/gmail-thread.tsx",
    "faces/family.tsx",
    "faces/fixtures/gmail-bulk-change.json",
    "faces/fixtures/gmail-decision.json",
    "faces/fixtures/gmail-draft.json",
    "faces/fixtures/gmail-list.json",
    "faces/fixtures/gmail-message.json",
    "faces/fixtures/gmail-reply.json",
    "faces/fixtures/gmail-thread.json",
    "html-text.test.ts",
    "package-lock.json",
    "package.json",
    "refusals.test.ts"
  ],
  "total_size": 227273,
  "checksum": "sha256:c449dee742ef1e15e31639032c3a02d6527fe1b18940be2cb39f8f355fb417c3",
  "built_at": "2026-09-09T22:09:26.753Z",
  "openapi": {
    "openapi": "3.1.0",
    "info": {
      "title": "snappy-gmail",
      "version": "0.0.0",
      "description": "Gmail as the machine's own hands -- read the inbox, threads and one message straight from Gmail's REST API with an access token minted at call time (no press binary, no Xano, no connector, no local mirror), draft with the same road, and SEND only through the Snappy stage door so the owner's approval and the receipt exist. One resolver picks the credential road -- the personal refresh-token grant first, the service account when that grant does not mint -- and every answer names the mailbox and the road it used. Works on any Mac whose env cache holds one of those two roads. Triggers on: gmail, my inbox, read my email, gmail thread, gmail draft, send email, mailbox."
    },
    "x-snappy-transport": "cli",
    "servers": [
      {
        "url": "npx tsx ~/.claude/skills/snappy-gmail/api.ts",
        "description": "The hand itself. Every path is a verb word, every query or body key an argument."
      }
    ],
    "paths": {
      "/list": {
        "get": {
          "operationId": "snappy_gmail_list",
          "summary": "snappy-gmail list",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many messages to return, newest first",
                "default": 20,
                "maximum": 500,
                "x-snappy-position": 0
              }
            },
            {
              "name": "query",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string",
                "description": "A Gmail search expression, exactly as the Gmail search box takes it — `from:sam is:unread`",
                "x-snappy-position": 1
              }
            },
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/get": {
        "get": {
          "operationId": "snappy_gmail_get",
          "summary": "snappy-gmail get",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "message-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The Gmail message id, from a `list` row's `id`",
                "x-snappy-position": 0
              }
            },
            {
              "name": "format",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string",
                "description": "How much of the message to fetch",
                "enum": [
                  "full",
                  "metadata",
                  "minimal"
                ],
                "default": "metadata",
                "x-snappy-position": 1
              }
            },
            {
              "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_gmail_thread",
          "summary": "snappy-gmail thread",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "thread-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The Gmail thread id, from a `list` row's `threadId`",
                "x-snappy-position": 0
              }
            },
            {
              "name": "source",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string",
                "description": "Which road answers. There is one road now — Gmail itself — and the word is kept because callers already spell it",
                "enum": [
                  "auto",
                  "live"
                ],
                "default": "auto",
                "x-snappy-position": 1
              }
            },
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many messages of that conversation to return. The count is the FLAG --limit, never a positional word; default 20, ceiling 500",
                "default": 20,
                "minimum": 1,
                "maximum": 500,
                "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`."
            }
          }
        }
      },
      "/draft": {
        "post": {
          "operationId": "snappy_gmail_draft",
          "summary": "snappy-gmail draft",
          "x-snappy-effect": "draft",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "to": {
                      "type": "string",
                      "description": "The recipient's email address",
                      "x-snappy-position": 0
                    },
                    "subject": {
                      "type": "string",
                      "description": "The subject line, verbatim",
                      "x-snappy-position": 1
                    },
                    "text": {
                      "type": "string",
                      "description": "The body of the mail, verbatim",
                      "x-snappy-position": 2
                    },
                    "thread-id": {
                      "type": "string",
                      "description": "The conversation this draft answers, from a `list` row's `threadId`. Given, the draft is filed in that thread and `--json` shows the thread under it",
                      "x-snappy-position": 3
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "to",
                    "subject",
                    "text"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/reply": {
        "post": {
          "operationId": "snappy_gmail_reply",
          "summary": "snappy-gmail 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": {
                    "thread-id": {
                      "type": "string",
                      "description": "The Gmail thread id being answered, from a `list` row's `threadId`",
                      "x-snappy-position": 0
                    },
                    "text": {
                      "type": "string",
                      "description": "The reply, verbatim",
                      "x-snappy-position": 1
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "thread-id",
                    "text"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/send": {
        "post": {
          "operationId": "snappy_gmail_send",
          "summary": "snappy-gmail send",
          "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": {
                    "to": {
                      "type": "string",
                      "description": "The recipient's email address",
                      "x-snappy-position": 0
                    },
                    "subject": {
                      "type": "string",
                      "description": "The subject line, verbatim",
                      "x-snappy-position": 1
                    },
                    "text": {
                      "type": "string",
                      "description": "The body of the mail, verbatim",
                      "x-snappy-position": 2
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "to",
                    "subject",
                    "text"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/archive": {
        "post": {
          "operationId": "snappy_gmail_archive",
          "summary": "snappy-gmail archive",
          "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": {
                    "ids": {
                      "type": "string",
                      "description": "One Gmail message id, or several separated by commas, from a `list` row's `id`",
                      "x-snappy-position": 0
                    }
                  },
                  "required": [
                    "ids"
                  ]
                }
              }
            }
          },
          "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": [
      "GOOGLE_CLIENT_ID",
      "GOOGLE_CLIENT_SECRET",
      "GOOGLE_SERVICE_ACCOUNT_EMAIL",
      "GOOGLE_SERVICE_ACCOUNT_KEY"
    ],
    "x-snappy-vendor-spec": {
      "kind": "discovery",
      "url": "https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest",
      "operations": {
        "list": "gmail.users.messages.list",
        "get": "gmail.users.messages.get",
        "thread": "gmail.users.threads.get",
        "draft": "gmail.users.drafts.create",
        "reply": "gmail.users.messages.send",
        "send": "gmail.users.messages.send",
        "archive": "gmail.users.messages.modify"
      },
      "pinned": {
        "sha256": "9bea1ebc37a9f4ed1da0d3871652c9e99d2db12483bc1cd27c618970ce3e4e86",
        "checked_at": "2026-09-09T20:44:11Z",
        "version": "20260907"
      }
    }
  }
}