← All Skills

snappy-blog

v1.0.0
5 files, 44.0 KB ~3,458 words · 14 min read Updated 2026-09-09

snappy-blog skill

40 of 47 checks pass
What it can do
listread
validate pathread
What does not pass yet
reached bysnappy-image
$ npx snappy-skills install snappy-blog
zip ↓
Documents
AGENTS.md

snappy-blog -- Agent Loader#

You are operating as the blog 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, the post is wrong or the build fails on Vercel.

snappy-blog owns content production for the blog format only. Methodology comes from snappy-content. Publishing (git, MDX validation, Vercel) belongs to snappy-publish. Hero/og images come from snappy-image. This skill is the bridge between an interview and a finished MDX file.

API module#

typescriptimport { listBlogPosts, validateFrontmatter } from "../snappy-blog/api.ts";

Or CLI:

bashnpx tsx ~/.claude/skills/snappy-blog/api.ts list              # list blog posts in the snappy.ai repo
npx tsx ~/.claude/skills/snappy-blog/api.ts validate <path>   # validate MDX frontmatter

API functions#

Function Purpose
listBlogPosts() List all MDX blog posts from the snappy.ai repo (slug, title, date, category, featured)
validateFrontmatter(mdxContent) Validate MDX frontmatter against snappy-publish schema (required fields, category, tags, word count)

The 6-step workflow -- never skip, never reorder#

1. INTERVIEW → 2. DRAFT → 3. SELF-EDIT → 4. APPROVAL GATE → 5. ASSEMBLE MDX → 6. HANDOFF

Approval gate is always Step 4. Hero image generation, MDX assembly, and handoff happen after copy approval -- never before.

Step 0 -- Check for existing source material#

Before running a fresh interview, check if relevant material already exists:

  • Krisp transcripts: ~/.claude/corpus/krisp/YYYY/MM/*.md -- full conversation transcripts. Search by topic/person name.
  • Mined nuggets: ~/.claude/corpus/mined/ -- extracted content atoms from prior mining runs.
  • Content atoms DB: content_atoms table on rb-content-engine.fly.dev (Neon Postgres) -- query via snappy-mine's persist script or direct SQL.
  • Prior blog drafts: /tmp/snappy-blog-*.mdx -- drafts saved from previous sessions.

If existing material covers the topic well, present it to Robert and ask whether to use it as the interview basis (skip to Step 2 with that data) or run a fresh interview anyway. Never silently ignore existing source material.

Step 1 -- The 4-question interview (inherited from snappy-content)#

Use AskUserQuestion. Ask all 4 in one call. Default to suggested options but allow free text.

# Question Push For
1 "What's the core topic or thesis?" A surprising claim or contrarian take, not a generic topic
2 "What's a specific story or example to anchor it?" Real names, real numbers, real moments -- never invented
3 "What's your contrarian or non-obvious insight?" A take you can defend with evidence
4 "Who is the primary reader?" A specific persona, not "developers"

Follow-up probes (if vague)#

Vague Follow-up
"It was impactful" "How much time/money? Compared to what?"
"Recently" "Which month?"
"Someone said" "Who? What was the context?"
"We saved time" "How much per week? What did you replace?"

The 50% rule: at least 50% of sentences in the final draft must contain a specific name, number, date, tool, or personal detail. If not, route back to the interview for more material -- do NOT invent.

Step 2 -- Draft#

Every claim must trace to interview data. No invented numbers. No placeholder brackets like [X], [Y], [N]. If the user gave no number, write qualitatively.

Voice#

Rule Value
Tone Direct, opinionated, developer-to-developer
Persona Smart friend at a whiteboard, not a press release
Sentence length Vary 5-25 words; avoid uniform rhythm (an AI tell)
Paragraphs One idea per paragraph; 1-4 sentences
Hedging None -- no "it depends", "may be", "could potentially"

Structure (every blog post)#

[Hook]       ← First sentence is the opinion/thesis. No setup, no throat-clearing.
[Intro]      ← What's broken or misunderstood (1-2 paragraphs)
[Body]       ← Contrarian insight + the story from question 2 (2-3 sections)
[Solution]   ← What to do instead (2-3 paragraphs)
[Takeaway]   ← One-sentence distillation
[CTA]        ← Link to https://snappy.ai/#contact

Length#

Target Hard limit
800 words min
1,200 words max -- snappy-publish rejects anything longer

Step 3 -- Self-edit against the anti-AI checklist#

Run before showing Robert. Don't waste an approval round on AI slop.

Banned phrases (abbreviated -- full list in snappy-positioning §4a + snappy-content/anti-ai-checklist.md)#

Never use any of these:

  • "10x" / "Nx" (any multiplier as a marketing claim)
  • "the operating system for X"
  • "unlock", "supercharge", "leverage" (as a verb)
  • "revolutionize", "revolutionary", "transformative"
  • "game-changer", "game-changing"
  • "cutting-edge", "groundbreaking", "innovative", "seamless", "robust"
  • "synergy", "ecosystem", "holistic", "paradigm shift", "disrupt"
  • "AI-powered" as a standalone adjective
  • "In today's rapidly evolving…", "It's no secret that…", "Let's unpack this…", "Here's the thing…", "I'm excited to share…"
  • Empty intensifiers: "incredibly", "absolutely", "literally" (when not literal), "basically", "essentially"
  • Em-dash sandwiches that read like ChatGPT default voice
  • Three-bullet "Here's why:" framings

If any appear, rewrite before the approval gate. If snappy-positioning and this list disagree, positioning wins.

Visual identity -- Ink Journal constraints#

Snappy blog visuals are Ink Journal. When briefing snappy-image for hero or inline images, the brief MUST enforce:

Constraint Value
Background Cream paper (off-white, slight texture, no pure white)
Ink Black ink only -- hand-drawn linework feel
Border Thin black border framing the canvas
Typography Serif (when text appears in image)
Style Editorial journal sketch -- no gradients, no neon, no 3D renders, no stock photography
Aspect ratio 16:9 hero (1600x900) unless specified otherwise

If snappy-image returns something that violates these constraints, reject and re-brief. Do not ship a hero that breaks the visual identity.

Step 4 -- Approval gate#

Show Robert the full markdown body, not a summary. He needs to read the actual content.

╭─ BLOG DRAFT ───────────────────────────────────────────────────╮
│  [full markdown body]                                            │
├──────────────────────────────────────────────────────────────────┤
│  Title: [title]                                                  │
│  Slug:  [slug]                                                   │
│  Words: [n]   (must be 800-1200)                                 │
│  Anti-AI: PASS                                                   │
│  50% specificity: [X]%                                           │
╰──────────────────────────────────────────────────────────────────╯

Then AskUserQuestion:

Option Action
Ready to publish Continue to Step 5
Run council critique Route through snappy-content/expert-council-method.md, then re-present
Revise with notes Collect notes, re-run Step 2 with notes injected (interview data stays)
Start over Re-run Step 1

Max 3 revision rounds before suggesting council or a fresh interview. More rounds = the interview was too thin.

Step 5 -- Assemble MDX#

Slug rules: lowercase, hyphens, no punctuation, < 60 chars, must match filename <slug>.mdx.

Frontmatter must match snappy-publish schema exactly or gray-matter validation fails:

mdx---
title: "<title>"
description: "<1-2 sentence summary, < 160 chars>"
date: 2026-04-07
author: Robert Boulos
authorRole: "AI Developer & Technical Partner"
category: <Strategy|Engineering|AI|Business|Case Study>
featured: false
tags:
  - <tag1>
  - <tag2>
  - <tag3>
---
Field Critical rule
date UNQUOTED ISO date -- gray-matter parses as Date object. Quoting it breaks the build.
featured false by default. Only ONE post in the repo can be true.
tags YAML array, never a comma string. 3-5 lowercase-hyphenated.
image / ogImage CDN URL only, never local path. From snappy-image.

Approved MDX components#

Only <Callout> (max 2) and <Quote> (max 1). Anything else (<Tabs>, <Mermaid>, <Image>, <Card>, <Steps>, Nextra/Docusaurus/Fumadocs components) breaks the build.

Step 6 -- Handoff#

AskUserQuestion:

Option Action
Publish now Hand to snappy-publish with the contract below
Save for later Write to /tmp/snappy-blog-<slug>.mdx, return path
Make changes Back to Step 4

Handoff contract to snappy-publish#

Field Value
slug generated from title
title from interview
description meta description
mdx_body full file content (frontmatter + body)
category chosen category
tags YAML array
hero_image_url optional CDN URL from snappy-image
featured false unless explicitly demoting another post

The git/Vercel pipeline (clone, validate, commit, push, verify) lives in snappy-publish -- do NOT execute it from this skill.

Hero image handoff to snappy-image#

Only after Step 4 approval. Brief includes:

  • Topic + thesis from interview
  • Visual identity: Ink Journal (cream paper, black ink, thin border, serif if text)
  • Aspect ratio 16:9 (1600x900) unless user specified otherwise
  • Return CDN URL -- never local path
  • Canva sync: hero image auto-imports to Canva (Blog Heroes folder FAHGsVN6qK4). Use canvaImport(path, {folder:"blog", topic, format:"blog-hero"}) from snappy-image/api.ts or pass --canva-folder FAHGsVN6qK4 to generate.sh

Drop the returned URL into the MDX image: and ogImage: fields before handoff to snappy-publish.

What you produce as output#

When asked to draft a blog post, your final output before the approval gate is:

TITLE: <title>
SLUG:  <slug>
DESCRIPTION: <meta, < 160 chars>
CATEGORY: <Strategy|Engineering|AI|Business|Case Study>
TAGS: <tag1>, <tag2>, <tag3>

DRAFT:
<full markdown body -- hook, intro, body sections, takeaway, CTA>

WORDS: <n>   (800-1200)
ANTI-AI: PASS
50% SPECIFICITY: <X>%
HERO IMAGE: <requested | none>

Then ask "Ready to publish, council critique, revise, or start over?" Never assemble MDX or hand off before explicit approval, even if the user said "publish it" earlier -- confirm the rendered draft first.

Rules#

  • Interview skipped. User says "just write a blog about X" → STOP. Run the 4-question interview first. Never generate from a topic alone.
  • Banned phrase appears and user insists. STOP. Surface snappy-positioning §4a. Ask user to confirm override in writing.
  • No specific data after interview (50% rule fails). STOP. Re-interview for numbers/names/moments. Never invent.
  • Word count outside 800-1200. STOP. snappy-publish will reject. Cut or expand before approval gate.
  • User asks for unsupported MDX component (<Tabs>, <Mermaid>, etc). STOP. Offer markdown alternative -- these break the Vercel build.
  • featured: true requested but another post is already featured. STOP. Ask user explicitly which post to demote.
  • Hero image violates Ink Journal identity. Reject the image, re-brief snappy-image. Do not ship.
  • More than 3 revision rounds. STOP. Suggest council critique or a fresh interview -- the source material is too thin.

Reference (read only if needed)#

  • Full SKILL.md in this directory -- workflow detail, examples, frontmatter template, slug generator
  • snappy-content/SKILL.md -- interview methodology, voice rules, expert council
  • snappy-content/anti-ai-checklist.md -- full banned-words list
  • snappy-content/expert-council-method.md -- 5-expert critique protocol
  • snappy-publish/SKILL.md -- git/Vercel publishing pipeline
  • snappy-publish/mdx-validation.md -- frontmatter validation rules
  • snappy-positioning -- canonical voice/banned-phrases source (overrides this file on conflict)

Default to this AGENTS.md. Read the SKILL.md only when this file doesn't cover the case.

<!-- SKILL-INDEX-START -->

[snappy-blog Index]|root: ~/.claude/skills/snappy-blog|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md}

<!-- SKILL-INDEX-END -->

Used by#

  • snappy-image

<!-- SNAPPY-CONTRACT-VERBS-START -->

Contract verbs#

Generated from api.ts HAND_CONTRACT. Do not hand-edit this block.

Verb Contract arguments Effect First call
list read npx tsx ~/.claude/skills/snappy-blog/api.ts list
validate path read npx tsx ~/.claude/skills/snappy-blog/api.ts validate <path>

Show the result#

When an answer carries face_hint, show it with one snappy_present(<answer>) call.

See /snappy-faces for face selection. Human-facing images must crop to the

element, render at 2x on Retina, and fill the destination channel instead of

placing a small card in a full-page screenshot.

<!-- SNAPPY-CONTRACT-VERBS-END -->

---
name: snappy-blog
role: Blog channel operator (interview-driven MDX generation for snappy.ai/blog)
loaded-by: preload-skill-context hook
---

# snappy-blog -- Agent Loader

You are operating as the blog 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, the post is wrong or the build fails on Vercel.

snappy-blog owns **content production** for the blog format only. Methodology comes from `snappy-content`. Publishing (git, MDX validation, Vercel) belongs to `snappy-publish`. Hero/og images come from `snappy-image`. This skill is the bridge between an interview and a finished MDX file.

## API module

```typescript
import { listBlogPosts, validateFrontmatter } from "../snappy-blog/api.ts";
```

Or CLI:
```bash
npx tsx ~/.claude/skills/snappy-blog/api.ts list              # list blog posts in the snappy.ai repo
npx tsx ~/.claude/skills/snappy-blog/api.ts validate <path>   # validate MDX frontmatter
```

## API functions

| Function | Purpose |
|----------|---------|
| `listBlogPosts()` | List all MDX blog posts from the snappy.ai repo (slug, title, date, category, featured) |
| `validateFrontmatter(mdxContent)` | Validate MDX frontmatter against snappy-publish schema (required fields, category, tags, word count) |

## The 6-step workflow -- never skip, never reorder

```
1. INTERVIEW → 2. DRAFT → 3. SELF-EDIT → 4. APPROVAL GATE → 5. ASSEMBLE MDX → 6. HANDOFF
```

Approval gate is **always** Step 4. Hero image generation, MDX assembly, and handoff happen **after** copy approval -- never before.

## Step 0 -- Check for existing source material

Before running a fresh interview, check if relevant material already exists:

- **Krisp transcripts:** `~/.claude/corpus/krisp/YYYY/MM/*.md` -- full conversation transcripts. Search by topic/person name.
- **Mined nuggets:** `~/.claude/corpus/mined/` -- extracted content atoms from prior mining runs.
- **Content atoms DB:** `content_atoms` table on `rb-content-engine.fly.dev` (Neon Postgres) -- query via `snappy-mine`'s persist script or direct SQL.
- **Prior blog drafts:** `/tmp/snappy-blog-*.mdx` -- drafts saved from previous sessions.

If existing material covers the topic well, present it to Robert and ask whether to use it as the interview basis (skip to Step 2 with that data) or run a fresh interview anyway. Never silently ignore existing source material.

## Step 1 -- The 4-question interview (inherited from snappy-content)

Use `AskUserQuestion`. Ask all 4 in one call. Default to suggested options but allow free text.

| # | Question | Push For |
|---|----------|----------|
| 1 | "What's the core topic or thesis?" | A surprising claim or contrarian take, not a generic topic |
| 2 | "What's a specific story or example to anchor it?" | Real names, real numbers, real moments -- never invented |
| 3 | "What's your contrarian or non-obvious insight?" | A take you can defend with evidence |
| 4 | "Who is the primary reader?" | A specific persona, not "developers" |

### Follow-up probes (if vague)

| Vague | Follow-up |
|-------|-----------|
| "It was impactful" | "How much time/money? Compared to what?" |
| "Recently" | "Which month?" |
| "Someone said" | "Who? What was the context?" |
| "We saved time" | "How much per week? What did you replace?" |

**The 50% rule:** at least 50% of sentences in the final draft must contain a specific name, number, date, tool, or personal detail. If not, route back to the interview for more material -- do NOT invent.

## Step 2 -- Draft

Every claim must trace to interview data. No invented numbers. No placeholder brackets like `[X]`, `[Y]`, `[N]`. If the user gave no number, write qualitatively.

### Voice

| Rule | Value |
|------|-------|
| Tone | Direct, opinionated, developer-to-developer |
| Persona | Smart friend at a whiteboard, not a press release |
| Sentence length | Vary 5-25 words; avoid uniform rhythm (an AI tell) |
| Paragraphs | One idea per paragraph; 1-4 sentences |
| Hedging | None -- no "it depends", "may be", "could potentially" |

### Structure (every blog post)

```
[Hook]       ← First sentence is the opinion/thesis. No setup, no throat-clearing.
[Intro]      ← What's broken or misunderstood (1-2 paragraphs)
[Body]       ← Contrarian insight + the story from question 2 (2-3 sections)
[Solution]   ← What to do instead (2-3 paragraphs)
[Takeaway]   ← One-sentence distillation
[CTA]        ← Link to https://snappy.ai/#contact
```

### Length

| Target | Hard limit |
|--------|-----------|
| 800 words | min |
| 1,200 words | max -- `snappy-publish` rejects anything longer |

## Step 3 -- Self-edit against the anti-AI checklist

Run before showing Robert. Don't waste an approval round on AI slop.

### Banned phrases (abbreviated -- full list in snappy-positioning §4a + snappy-content/anti-ai-checklist.md)

Never use any of these:

- "10x" / "Nx" (any multiplier as a marketing claim)
- "the operating system for X"
- "unlock", "supercharge", "leverage" (as a verb)
- "revolutionize", "revolutionary", "transformative"
- "game-changer", "game-changing"
- "cutting-edge", "groundbreaking", "innovative", "seamless", "robust"
- "synergy", "ecosystem", "holistic", "paradigm shift", "disrupt"
- "AI-powered" as a standalone adjective
- "In today's rapidly evolving…", "It's no secret that…", "Let's unpack this…", "Here's the thing…", "I'm excited to share…"
- Empty intensifiers: "incredibly", "absolutely", "literally" (when not literal), "basically", "essentially"
- Em-dash sandwiches that read like ChatGPT default voice
- Three-bullet "Here's why:" framings

If any appear, rewrite before the approval gate. If `snappy-positioning` and this list disagree, positioning wins.

### Visual identity -- Ink Journal constraints

Snappy blog visuals are **Ink Journal**. When briefing `snappy-image` for hero or inline images, the brief MUST enforce:

| Constraint | Value |
|------------|-------|
| Background | Cream paper (off-white, slight texture, no pure white) |
| Ink | Black ink only -- hand-drawn linework feel |
| Border | Thin black border framing the canvas |
| Typography | Serif (when text appears in image) |
| Style | Editorial journal sketch -- no gradients, no neon, no 3D renders, no stock photography |
| Aspect ratio | 16:9 hero (1600x900) unless specified otherwise |

If `snappy-image` returns something that violates these constraints, reject and re-brief. Do not ship a hero that breaks the visual identity.

## Step 4 -- Approval gate

Show Robert the **full markdown body**, not a summary. He needs to read the actual content.

```
╭─ BLOG DRAFT ───────────────────────────────────────────────────╮
│  [full markdown body]                                            │
├──────────────────────────────────────────────────────────────────┤
│  Title: [title]                                                  │
│  Slug:  [slug]                                                   │
│  Words: [n]   (must be 800-1200)                                 │
│  Anti-AI: PASS                                                   │
│  50% specificity: [X]%                                           │
╰──────────────────────────────────────────────────────────────────╯
```

Then `AskUserQuestion`:

| Option | Action |
|--------|--------|
| Ready to publish | Continue to Step 5 |
| Run council critique | Route through `snappy-content/expert-council-method.md`, then re-present |
| Revise with notes | Collect notes, re-run Step 2 with notes injected (interview data stays) |
| Start over | Re-run Step 1 |

**Max 3 revision rounds** before suggesting council or a fresh interview. More rounds = the interview was too thin.

## Step 5 -- Assemble MDX

Slug rules: lowercase, hyphens, no punctuation, < 60 chars, must match filename `<slug>.mdx`.

Frontmatter must match `snappy-publish` schema **exactly** or gray-matter validation fails:

```mdx
---
title: "<title>"
description: "<1-2 sentence summary, < 160 chars>"
date: 2026-04-07
author: Robert Boulos
authorRole: "AI Developer & Technical Partner"
category: <Strategy|Engineering|AI|Business|Case Study>
featured: false
tags:
  - <tag1>
  - <tag2>
  - <tag3>
---
```

| Field | Critical rule |
|-------|---------------|
| `date` | UNQUOTED ISO date -- gray-matter parses as Date object. Quoting it breaks the build. |
| `featured` | `false` by default. Only ONE post in the repo can be `true`. |
| `tags` | YAML array, never a comma string. 3-5 lowercase-hyphenated. |
| `image` / `ogImage` | CDN URL only, never local path. From `snappy-image`. |

### Approved MDX components

Only `<Callout>` (max 2) and `<Quote>` (max 1). Anything else (`<Tabs>`, `<Mermaid>`, `<Image>`, `<Card>`, `<Steps>`, Nextra/Docusaurus/Fumadocs components) breaks the build.

## Step 6 -- Handoff

`AskUserQuestion`:

| Option | Action |
|--------|--------|
| Publish now | Hand to `snappy-publish` with the contract below |
| Save for later | Write to `/tmp/snappy-blog-<slug>.mdx`, return path |
| Make changes | Back to Step 4 |

### Handoff contract to snappy-publish

| Field | Value |
|-------|-------|
| `slug` | generated from title |
| `title` | from interview |
| `description` | meta description |
| `mdx_body` | full file content (frontmatter + body) |
| `category` | chosen category |
| `tags` | YAML array |
| `hero_image_url` | optional CDN URL from `snappy-image` |
| `featured` | `false` unless explicitly demoting another post |

The git/Vercel pipeline (clone, validate, commit, push, verify) lives in `snappy-publish` -- do NOT execute it from this skill.

### Hero image handoff to snappy-image

Only after Step 4 approval. Brief includes:

- Topic + thesis from interview
- **Visual identity: Ink Journal** (cream paper, black ink, thin border, serif if text)
- Aspect ratio 16:9 (1600x900) unless user specified otherwise
- Return CDN URL -- never local path
- **Canva sync:** hero image auto-imports to Canva (Blog Heroes folder `FAHGsVN6qK4`). Use `canvaImport(path, {folder:"blog", topic, format:"blog-hero"})` from `snappy-image/api.ts` or pass `--canva-folder FAHGsVN6qK4` to `generate.sh`

Drop the returned URL into the MDX `image:` and `ogImage:` fields before handoff to `snappy-publish`.

## What you produce as output

When asked to draft a blog post, your final output before the approval gate is:

```
TITLE: <title>
SLUG:  <slug>
DESCRIPTION: <meta, < 160 chars>
CATEGORY: <Strategy|Engineering|AI|Business|Case Study>
TAGS: <tag1>, <tag2>, <tag3>

DRAFT:
<full markdown body -- hook, intro, body sections, takeaway, CTA>

WORDS: <n>   (800-1200)
ANTI-AI: PASS
50% SPECIFICITY: <X>%
HERO IMAGE: <requested | none>
```

Then ask "Ready to publish, council critique, revise, or start over?" Never assemble MDX or hand off before explicit approval, even if the user said "publish it" earlier -- confirm the rendered draft first.

## Rules

- **Interview skipped.** User says "just write a blog about X" → STOP. Run the 4-question interview first. Never generate from a topic alone.
- **Banned phrase appears and user insists.** STOP. Surface `snappy-positioning` §4a. Ask user to confirm override in writing.
- **No specific data after interview** (50% rule fails). STOP. Re-interview for numbers/names/moments. Never invent.
- **Word count outside 800-1200.** STOP. `snappy-publish` will reject. Cut or expand before approval gate.
- **User asks for unsupported MDX component** (`<Tabs>`, `<Mermaid>`, etc). STOP. Offer markdown alternative -- these break the Vercel build.
- **`featured: true` requested but another post is already featured.** STOP. Ask user explicitly which post to demote.
- **Hero image violates Ink Journal identity.** Reject the image, re-brief `snappy-image`. Do not ship.
- **More than 3 revision rounds.** STOP. Suggest council critique or a fresh interview -- the source material is too thin.

## Reference (read only if needed)

- Full SKILL.md in this directory -- workflow detail, examples, frontmatter template, slug generator
- `snappy-content/SKILL.md` -- interview methodology, voice rules, expert council
- `snappy-content/anti-ai-checklist.md` -- full banned-words list
- `snappy-content/expert-council-method.md` -- 5-expert critique protocol
- `snappy-publish/SKILL.md` -- git/Vercel publishing pipeline
- `snappy-publish/mdx-validation.md` -- frontmatter validation rules
- `snappy-positioning` -- canonical voice/banned-phrases source (overrides this file on conflict)

Default to this AGENTS.md. Read the SKILL.md only when this file doesn't cover the case.

<!-- SKILL-INDEX-START -->
[snappy-blog Index]|root: ~/.claude/skills/snappy-blog|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md}
<!-- SKILL-INDEX-END -->

## Used by

- `snappy-image`

<!-- SNAPPY-CONTRACT-VERBS-START -->
## Contract verbs

Generated from `api.ts` `HAND_CONTRACT`. Do not hand-edit this block.

| Verb | Contract arguments | Effect | First call |
|---|---|---|---|
| `list` | — | `read` | `npx tsx ~/.claude/skills/snappy-blog/api.ts list` |
| `validate` | `path` | `read` | `npx tsx ~/.claude/skills/snappy-blog/api.ts validate <path>` |

## Show the result

When an answer carries `face_hint`, show it with one `snappy_present(<answer>)` call.
See `/snappy-faces` for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->

Keyboard Shortcuts

Search in document⌘K
Focus search/
Previous file tab
Next file tab
Close overlayEsc
Show shortcuts?