{
  "schema": "snappy-card/1",
  "skill": "snappy-update",
  "tier": "public",
  "origin_tier": "base",
  "purpose": "Formats and delivers structured project updates to consulting clients. Pulls raw git",
  "verbs": [
    {
      "name": "commits",
      "args": [
        "repo?",
        "days?"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-update/api.ts commits"
    },
    {
      "name": "notify",
      "args": [
        "channel-id",
        "text"
      ],
      "effect": "send",
      "firstCall": "npx tsx ~/.claude/skills/snappy-update/api.ts notify <channel-id> \"<text>\""
    }
  ],
  "faces": [],
  "agents": [],
  "uses": [],
  "used_by": [
    "snappy-slack"
  ],
  "checks": {
    "pass": 40,
    "fail": 7,
    "defer": 14,
    "line": "40 of 47 checks pass",
    "failing": [
      {
        "id": 4,
        "rule": "Near-neighbour descriptions cross-reference each other",
        "says": "near neighbours not named: snappy-specwatch"
      },
      {
        "id": 8,
        "rule": "Skill body and reference graph stay within disclosure budgets",
        "says": "399 lines; ~4732 tokens; TEMPLATES.md >100 lines without ToC; TEMPLATES.md >100 lines without ToC; TEMPLATES.md >100 lines without ToC; TEMPLATES.md >100 lines without ToC; TEMPLATES.md >100 lines without ToC; TEMPLATES.md >100 lines without ToC; deep reference ../snappy-infra/auth-reference.md"
      },
      {
        "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 ~185 tokens; body ~4732 tokens"
      },
      {
        "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",
    "TEMPLATES.md",
    "api.ts",
    "doors.test.ts",
    "evidence.test.ts",
    "read-limit.test.ts",
    "refusals.test.ts",
    "scripts/dev-update.sh"
  ],
  "total_size": 61145,
  "checksum": "sha256:71be5f0e9035794b464958a817514c6b718e7759106a855831bbefc3e7ef06d8",
  "built_at": "2026-09-09T22:11:19.610Z",
  "openapi": {
    "openapi": "3.1.0",
    "info": {
      "title": "snappy-update",
      "version": "0.0.0",
      "description": "Snappy Update -- dev updates to consulting clients. Owns the format and delivery of weekly Friday standups, Monday sprint plans, milestone shipped notifications, bug fix updates, and deployment notices. Pulls git history from client repos, formats per snappy-content voice rules, and routes through each client's preferred channel via snappy-clients lookup. Triggers on: dev update, client update, status update, standup, weekly standup, friday update, monday plan, sprint planning, milestone, milestone shipped, deployment notification, send update, bug fix update, hotfix notice, ship notification, smart channel routing, notify client, update all clients, weekly dev update, client comms cadence, async update, release notes for client."
    },
    "x-snappy-transport": "cli",
    "servers": [
      {
        "url": "npx tsx ~/.claude/skills/snappy-update/api.ts",
        "description": "The hand itself. Every path is a verb word, every query or body key an argument."
      }
    ],
    "paths": {
      "/commits": {
        "get": {
          "operationId": "snappy_update_commits",
          "summary": "snappy-update commits",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "repo",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string",
                "description": "GitHub repository as owner/name",
                "x-snappy-position": 0
              }
            },
            {
              "name": "days",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many days of commits to summarise",
                "x-snappy-position": 1
              }
            },
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many commits to return, newest first. 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`."
            }
          }
        }
      },
      "/notify": {
        "post": {
          "operationId": "snappy_update_notify",
          "summary": "snappy-update notify",
          "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": {
                    "channel-id": {
                      "type": "string",
                      "description": "Destination channel id",
                      "x-snappy-position": 0
                    },
                    "text": {
                      "type": "string",
                      "description": "The words to use, verbatim",
                      "x-snappy-position": 1
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "channel-id",
                    "text"
                  ]
                }
              }
            }
          },
          "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": [
      "GITHUB_TOKEN",
      "SLACK_BOT_TOKEN",
      "TELEGRAM_BOT_TOKEN",
      "TELEGRAM_ROBERT_CHAT_ID"
    ]
  }
}