{
  "schema": "snappy-card/1",
  "skill": "snappy-linkedin",
  "tier": "public",
  "origin_tier": "base",
  "purpose": "You are operating as the LinkedIn channel for Snappy. This file is the operational contract. The full SKILL.md exists for reference but the rules below are load-bearing -- if you deviate from them, the post is wrong.",
  "verbs": [
    {
      "name": "profile",
      "args": [],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts profile"
    },
    {
      "name": "scheduled",
      "args": [],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts scheduled"
    },
    {
      "name": "token-status",
      "args": [],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts token-status"
    },
    {
      "name": "metrics",
      "args": [
        "name"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts metrics \"<name>\""
    },
    {
      "name": "post",
      "args": [
        "text",
        "image?"
      ],
      "effect": "post",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts post \"<text>\""
    },
    {
      "name": "image-upload",
      "args": [
        "image"
      ],
      "effect": "draft",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts image-upload <image>"
    },
    {
      "name": "schedule",
      "args": [
        "text",
        "when"
      ],
      "effect": "post",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts schedule \"<text>\" <when>"
    },
    {
      "name": "comments",
      "args": [
        "post_urn"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts comments <post_urn>"
    },
    {
      "name": "thread",
      "args": [
        "post_urn"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts thread <post_urn>"
    },
    {
      "name": "comment",
      "args": [
        "post_urn",
        "text"
      ],
      "effect": "post",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts comment <post_urn> \"<text>\""
    },
    {
      "name": "draft-get",
      "args": [
        "draft-id"
      ],
      "effect": "read",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts draft-get <draft-id>"
    },
    {
      "name": "draft-patch",
      "args": [
        "draft-id",
        "text"
      ],
      "effect": "write-reversible",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts draft-patch <draft-id> \"<text>\""
    },
    {
      "name": "draft-delete",
      "args": [
        "draft-id"
      ],
      "effect": "delete",
      "firstCall": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts draft-delete <draft-id>"
    }
  ],
  "faces": [],
  "agents": [],
  "uses": [],
  "used_by": [
    "snappy-image",
    "snappy-remotion"
  ],
  "checks": {
    "pass": 45,
    "fail": 7,
    "defer": 9,
    "line": "45 of 52 checks pass",
    "failing": [
      {
        "id": 8,
        "rule": "Skill body and reference graph stay within disclosure budgets",
        "says": "447 lines; ~5932 tokens; browser-reference.md >100 lines without ToC; browser-reference.md >100 lines without ToC"
      },
      {
        "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": 40,
        "rule": "Metadata, skill body, and resources follow three-level budgets",
        "says": "metadata ~238 tokens; body ~5932 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",
    "adapter.ts",
    "api.ts",
    "browser-reference.md",
    "comment-road.ts",
    "content-calendar.md",
    "content-formats.md",
    "daily-routine.md",
    "entities.json",
    "face.test.ts",
    "faces/components/linkedin-decision.tsx",
    "faces/components/linkedin-face.css",
    "faces/components/linkedin-post.tsx",
    "faces/components/linkedin-social.tsx",
    "faces/family.tsx",
    "faces/fixtures/linkedin-comments.json",
    "faces/fixtures/linkedin-decision.json",
    "faces/fixtures/linkedin-feed.json",
    "faces/fixtures/linkedin-post-published.json",
    "faces/fixtures/linkedin-post.json",
    "faces/fixtures/linkedin-profile.json",
    "image-road.ts",
    "linkedin-wire.ts",
    "metrics.json",
    "outreach.md",
    "posting-types.md",
    "profile-optimization.md",
    "read-limit.test.ts",
    "refusals.test.ts",
    "scripts/like-queue.sh",
    "scripts/linkedin-post.sh",
    "scripts/typefully.sh",
    "stage.test.ts"
  ],
  "total_size": 301687,
  "checksum": "sha256:d845da30e1a626e59a0a68dee396e39027789d517d780a563658e3dfdf933afc",
  "built_at": "2026-09-09T22:09:48.416Z",
  "openapi": {
    "openapi": "3.1.0",
    "info": {
      "title": "snappy-linkedin",
      "version": "0.0.0",
      "description": "LinkedIn operations for Snappy -- posting (text, image, carousel, native video, article, poll, document, comment), outreach sequences (5-step Day 0/3/7/14), profile optimization, engagement tracking, daily routine, inbox triage, content repurposing from blog/youtube/email/skool, and the LinkedIn → snappy-sales outbound funnel hand-off. Posting via Xano API; browsing, messaging, and engagement via agent-browser. Triggers on: linkedin, post on linkedin, share on linkedin, linkedin post, linkedin outreach, connection request, linkedin message, dm on linkedin, check linkedin, linkedin engagement, linkedin routine, linkedin daily, linkedin notifications, linkedin profile, optimize linkedin, linkedin carousel, linkedin video, linkedin article, linkedin poll, linkedin comment, comment on linkedin, repurpose for linkedin, linkedin prospect, linkedin follow up, linkedin comments, who is this person, linkedin search, linkedin lead, linkedin inbox."
    },
    "x-snappy-transport": "cli",
    "servers": [
      {
        "url": "npx tsx ~/.claude/skills/snappy-linkedin/api.ts",
        "description": "The hand itself. Every path is a verb word, every query or body key an argument."
      }
    ],
    "paths": {
      "/profile": {
        "get": {
          "operationId": "snappy_linkedin_profile",
          "summary": "snappy-linkedin profile",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "json",
              "in": "query",
              "required": false,
              "schema": {
                "x-snappy-flag": "--json"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/scheduled": {
        "get": {
          "operationId": "snappy_linkedin_scheduled",
          "summary": "snappy-linkedin scheduled",
          "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`."
            }
          }
        }
      },
      "/token-status": {
        "get": {
          "operationId": "snappy_linkedin_token_status",
          "summary": "snappy-linkedin 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`."
            }
          }
        }
      },
      "/metrics": {
        "get": {
          "operationId": "snappy_linkedin_metrics",
          "summary": "snappy-linkedin metrics",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "name",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "Which counter to compute",
                "enum": [
                  "polished-per-week",
                  "apply-rate",
                  "sweep-per-week"
                ],
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/post": {
        "post": {
          "operationId": "snappy_linkedin_post",
          "summary": "snappy-linkedin post",
          "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": {
                    "text": {
                      "type": "string",
                      "description": "The post's words, verbatim, as they will appear on the feed",
                      "x-snappy-position": 0
                    },
                    "image": {
                      "type": "string",
                      "description": "A local file path or an http(s) address this Computer can read; the bytes upload inside the approved post, never at staging time",
                      "x-snappy-position": 1
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "text"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/image-upload": {
        "post": {
          "operationId": "snappy_linkedin_image_upload",
          "summary": "snappy-linkedin image-upload",
          "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": {
                    "image": {
                      "type": "string",
                      "description": "A local file path or an http(s) address this Computer can read",
                      "x-snappy-position": 0
                    }
                  },
                  "required": [
                    "image"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/schedule": {
        "post": {
          "operationId": "snappy_linkedin_schedule",
          "summary": "snappy-linkedin schedule",
          "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": {
                    "text": {
                      "type": "string",
                      "description": "The post's words, verbatim",
                      "x-snappy-position": 0
                    },
                    "when": {
                      "type": "string",
                      "description": "When to publish, as an ISO 8601 instant — 2026-09-10T14:00:00Z",
                      "x-snappy-position": 1
                    }
                  },
                  "required": [
                    "text",
                    "when"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/comments": {
        "get": {
          "operationId": "snappy_linkedin_comments",
          "summary": "snappy-linkedin comments",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "post_urn",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The LinkedIn URN of the post whose discussion to read — urn:li:share:… or urn:li:ugcPost:…",
                "x-snappy-position": 0
              }
            },
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many comments of that discussion to read, 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`."
            }
          }
        }
      },
      "/thread": {
        "get": {
          "operationId": "snappy_linkedin_thread",
          "summary": "snappy-linkedin thread",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "post_urn",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The LinkedIn URN of the post whose discussion to read — urn:li:share:… or urn:li:ugcPost:…",
                "x-snappy-position": 0
              }
            },
            {
              "name": "limit",
              "in": "query",
              "required": false,
              "schema": {
                "type": "integer",
                "description": "How many comments of that discussion to read, 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`."
            }
          }
        }
      },
      "/comment": {
        "post": {
          "operationId": "snappy_linkedin_comment",
          "summary": "snappy-linkedin comment",
          "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": {
                    "post_urn": {
                      "type": "string",
                      "description": "The LinkedIn URN of the post being commented on — urn:li:share:… or urn:li:ugcPost:…",
                      "x-snappy-position": 0
                    },
                    "text": {
                      "type": "string",
                      "description": "The comment's words, verbatim",
                      "x-snappy-position": 1
                    },
                    "json": {
                      "x-snappy-flag": "--json"
                    }
                  },
                  "required": [
                    "post_urn",
                    "text"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/draft-get": {
        "get": {
          "operationId": "snappy_linkedin_draft_get",
          "summary": "snappy-linkedin draft-get",
          "x-snappy-effect": "read",
          "x-snappy-annotations": {
            "readOnlyHint": true,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "parameters": [
            {
              "name": "draft-id",
              "in": "query",
              "required": true,
              "schema": {
                "type": "string",
                "description": "The Typefully draft id, from a `scheduled` row's `id`",
                "x-snappy-position": 0
              }
            }
          ],
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/draft-patch": {
        "post": {
          "operationId": "snappy_linkedin_draft_patch",
          "summary": "snappy-linkedin draft-patch",
          "x-snappy-effect": "write-reversible",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft-id": {
                      "type": "string",
                      "description": "The Typefully draft id, from a `scheduled` row's `id`",
                      "x-snappy-position": 0
                    },
                    "text": {
                      "type": "string",
                      "description": "The draft's new words, verbatim; they replace the whole body",
                      "x-snappy-position": 1
                    }
                  },
                  "required": [
                    "draft-id",
                    "text"
                  ]
                }
              }
            }
          },
          "responses": {
            "200": {
              "description": "The verb's own answer, as printed by `--json`."
            }
          }
        }
      },
      "/draft-delete": {
        "post": {
          "operationId": "snappy_linkedin_draft_delete",
          "summary": "snappy-linkedin draft-delete",
          "x-snappy-effect": "delete",
          "x-snappy-annotations": {
            "readOnlyHint": false,
            "destructiveHint": true,
            "idempotentHint": false,
            "openWorldHint": true
          },
          "requestBody": {
            "required": true,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft-id": {
                      "type": "string",
                      "description": "The Typefully draft id, from a `scheduled` row's `id`",
                      "x-snappy-position": 0
                    }
                  },
                  "required": [
                    "draft-id"
                  ]
                }
              }
            }
          },
          "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": [
      "LINKEDIN_CLIENT_ID",
      "LINKEDIN_CLIENT_SECRET",
      "TYPEFULLY_API_KEY"
    ]
  }
}