Generate Pulumi event social cards

VerifiedSafe

Generates on-brand social cards for Pulumi events in five sizes using the Satori renderer, enriched with co-presenters and partner logos.

Sby Skills Guide Bot
ContentIntermediate
7/31/2026
Claude Code
#social-cards#opengraph#image-generation#satori#pulumi

Recommended for

Automated summary

Generates on-brand social cards for a Pulumi event or workshop in five sizes using the Satori renderer shared with the site build.

Strengths

  • Emits five sizes in one run (1200x628 OG, 1200x675, 628x628, 1080x1080, 540x960) with output identical to the build-time default card.
  • Enriches the card with speakers missing from frontmatter (external co-presenters and their photos) plus partner logos such as Pulumi and Microsoft.
  • Two clear modes: event-bound (writes PNGs into the bundle and sets meta_image / meta_image_square) or standalone (just PNGs in a folder, no frontmatter writes).
  • Non-interactive mode (-y / --non-interactive) auto-resolves people and logos and skips any unresolvable asset with a warning instead of blocking.

Limitations

  • Tightly coupled to the Pulumi repo: it assumes scripts/meta-images/render-event.mjs and the content/events/<slug>/ layout exist.
  • Photo and logo resolution relies on web search/fetch and can silently miss assets in unattended mode, where failures are only warned about.
  • Not always needed: most events already get an automatic on-brand card at build time, and no Register button is added unless explicitly requested.
When to use it

Use it when you need to create or enrich a Pulumi event's social card with speakers or logos that frontmatter does not cover.

When not to use it

Avoid it outside the Pulumi repository and shared renderer setup, or when the automatic build-time card is already sufficient.

Security analysis

Safe
Quality score88/100

The skill instructs running a local Node/Satori renderer script to generate image files and optionally update frontmatter and write PNGs into a repo bundle. It uses WebSearch/WebFetch to resolve people and logos, which is a legitimate network use for asset lookup, but no destructive commands, exfiltration, obfuscation, or credential handling are present. Writing files and updating frontmatter is a normal authoring action.

No concerns found

How this analysis is made

Examples

Event-bound regeneration
/event-meta-image content/events/seattle-kubernetes-workshop — regenerate the social cards and add our external co-presenter Jane Doe with her photo, plus the Microsoft partner logo.
Unattended run
/event-meta-image -y for the upcoming workshop event: derive the presenters and partner logos automatically and skip anything you can't resolve.
Standalone images
Generate event social card images for a Pulumi + Datadog workshop called 'Shipping Infra with TypeScript' — just emit the five PNGs to .context/event-images/infra-ts/, no frontmatter changes.

name: event-meta-image description: "Generate on-brand social cards for a Pulumi event/workshop in five sizes (1200x628 OG, 1200x675, 628x628, 1080x1080, 540x960) using the shared Satori events renderer. Enriches the build-time default with people not in frontmatter (external co-presenters + their photos) and company/partner logos (Pulumi + e.g. Microsoft). Runs event-bound (writes into a content/events/<slug>/ bundle + sets meta_image / meta_image_square) or standalone (just emit the five PNGs to a folder). Use when the user types /event-meta-image or asks to create, generate, regenerate, or enrich an event's meta image, social card, OpenGraph image, or speaker/workshop card. Pass -y / --yes / --non-interactive (or 'no questions') to run end-to-end with sensible defaults and no prompts. A Register button is never added unless explicitly requested."

/event-meta-image — Generate event / workshop social cards

You generate the branded event card (the Figma "Workshop / event images" component) in five sizes via the shared Node/Satori renderer — the same renderer the build uses for the automatic default, so output is identical to what ships, just enriched with inputs that aren't in frontmatter (external co-presenters, their photos, partner logos).

Skill directory: .claude/commands/event-meta-image/ — paths below are relative to the repo root unless noted. Renderer CLI: scripts/meta-images/render-event.mjs (shared with the build). One JSON config → one PNG per size.

You do not need a custom card for most events. Every event already gets an on-brand landscape + square card automatically at build time from its frontmatter (scripts/generate-meta-images.mjs). Use this skill only to (a) enrich a card with people/logos not in frontmatter, (b) override the auto default, or (c) grab the extra social sizes (square-large, portrait, tall) for manual use. (For a plain default with no enrichment, scripts/meta-images/render-event-cards.mjs <slug> renders all five sizes from frontmatter without a config.)


[Step 0/6] Pick the mode

  • Event-bound — there is a target content/events/<slug>/ page. Triggered when $ARGUMENTS names an event path/slug, when git status shows a changed event, or when the user points at one. Writes the PNGs into the page bundle and updates frontmatter.
  • Standalone — the user just wants images ("generate event images for …", no event attached). Collect inputs directly (Steps 3–5), skip frontmatter, and write the PNGs to an output dir (default .context/event-images/<slug>/, or a path the user gives). No frontmatter or bundle writes.

Everything below applies to both modes except the bundle/frontmatter writes in Step 6 (event-bound only).

Interaction mode — when to ask questions

Orthogonal to event-bound/standalone, pick how much to prompt. Default to searching, not asking: in both modes you still auto-resolve people and logos via the ladders in Steps 3–4 (including WebSearch/WebFetch). The modes differ only in what happens when something can't be resolved or is genuinely ambiguous.

  • Unattended — never ask. Trigger when $ARGUMENTS contains -y, --yes, --non-interactive, --no-input, no questions, or "just do it" (and automatically whenever you're running non-interactively, e.g. under claude -p or the eval, where AskUserQuestion can't be answered). Skip every AskUserQuestion. Derive all inputs automatically:
    • overline = event_type; title = frontmatter title; speakers = frontmatter presenters[].photo; additionalText = the derived "With …" line.
    • people = frontmatter presenters plus any external co-presenter named in the title/body.
    • logos = ["pulumi"] plus any partner company named in the title/body.
    • Auto-resolve each derived photo/logo via the Step 3 / Step 4 ladders. On a resolution miss, skip that one asset with a warning and keep going — never block. No button, no secondary title.
  • Default — ask only when needed. Same automatic derivation and search as unattended mode, but fall back to a single targeted AskUserQuestion only when an asset can't be resolved, or an input is genuinely ambiguous (multiple candidate events, missing title, a partner named but not identifiable). Do not ask to confirm things already known from frontmatter or $ARGUMENTS.

[Step 1/6] Locate the event (event-bound)

  1. If $ARGUMENTS contains an event path or slug, use content/events/<slug>/index.md.
  2. Otherwise run git status / git diff --name-only for changed content/events/*/index.md.
  3. Multiple candidates → ask with AskUserQuestion. None → ask for the slug/path (or switch to standalone if the user only wants loose images).

Read the full index.md (frontmatter + body).

[Step 2/6] Seed from frontmatter

Pull from frontmatter:

  • title (required — the card's main title; the renderer fits it to the box).
  • event_type → the default overline (uppercased automatically).
  • presenters: presenters: [{ name, role, photo }] — the in-frontmatter speakers. Each photo (e.g. /images/people/<x>.jpg or /images/team/<x>.jpg) resolves under static/.

The build default already uses exactly these. If the user only wants the default look in all five sizes (no new people/logos), you can skip straight to Step 6 with overline, title, the presenters' photo paths as speakers, the "With …" line as additionalText, and ["pulumi"] as logos.

[Step 3/6] Add people not in frontmatter (external co-presenters)

Add anyone beyond the presenters: list — e.g. a partner-company co-host. Derive these from the event first (the title/body and $ARGUMENTS); only fall back to AskUserQuestion per the interaction mode (default: ask when a person is named but you can't identify or resolve them; unattended: skip an unresolvable person with a warning, never ask). For each added person collect name + role/company, then resolve a color photo via this ladder (stop at the first hit):

  1. An existing presenters[].photo (already have it).
  2. static/images/team/<kebab-name>.{jpg,jpeg,png}.
  3. static/images/people/<kebab-name>.{jpg,jpeg,png}.
  4. Web: WebSearch for a headshot / company bio photo, then WebFetch the image. LinkedIn images are usually auth-gated — expect this to fail and fall through.
  5. Ask the user for a photo URL or local file path.

Save any fetched/provided external photo to a real file (e.g. .context/event-images/<slug>/<kebab-name>.jpg) and pass that path in the config — the renderer resolves local files and data: URIs, not remote URLs. Photos render color + circular on a violet ring (the renderer can't reproduce the Figma duotone; the ring keeps them on-brand). Up to 5 speakers display (the renderer shrinks the circles so they fit, stacking vertically on landscape and in a row on square/portrait); the byline lists every name. Beyond 5 is rare — pick the 5 to show in Step 5.

[Step 4/6] Company / partner logos

Pulumi is always included ("pulumi" → the bundled brand mark — never re-color or restyle it). The renderer lays the logos out in a row joined by a muted + (Pulumi + Microsoft + …), so each entry should read as a company on its own.

Add partner/company logos (e.g. Microsoft, GitHub, Google Cloud, NVIDIA) for any partner named in the event's title/body or $ARGUMENTS. Derive them from the event first — only fall back to AskUserQuestion per the interaction mode (default: ask when a partner is named but you can't source a compliant logo; unattended: skip an unresolvable logo with a warning, never ask). Three hard rules when sourcing each one:

  • Default to the WORDMARK — the lockup that includes the company name (e.g. the Microsoft squares plus "Microsoft"), not the bare glyph. A mark-only icon next to "Pulumi" reads as unfinished. Only fall back to a glyph if no wordmark exists or the glyph IS the brand (rare).
  • Prefer the HORIZONTAL variant. The renderer scales every logo to a fixed row height, so a vertical/stacked lockup (mark stacked above the name) renders its text tiny and unbalanced next to Pulumi. Choose the horizontal lockup (mark beside the name, or the wordmark alone). Rule of thumb: check the SVG viewBox / PNG dimensions and prefer an aspect ratio ≳ 3:1; reject stacked or roughly-square lockups (aspect ≲ 2) even when they're technically a wordmark, and keep looking (a company's brand/press page usually offers both a "horizontal" and a "stacked" variant — take the horizontal one).
  • Must be an SVG or a TRANSPARENT PNG. The card background is near-white (#f5f5ff), so a logo with a baked-in white/opaque background shows as an ugly box — never use one. Verify transparency before caching (a PNG reported by file as RGB with no alpha is opaque; RGBA or a colormap with tRNS may be transparent — check the render).

Resolve each via this ladder, stopping at the first hit that satisfies both rules:

  1. .claude/commands/event-meta-image/assets/logos/<name>.svg (cached — see references/logos.md).
  2. static/logos/** (the site's own brand assets — often clean SVG wordmarks).
  3. Web — WebSearch "<company> logo wordmark SVG transparent", then WebFetch/curl the official SVG. Good sources: the company's own brand/press page, Wikimedia Commons (*_logo.svg), and reputable vector libraries. simple-icons (https://raw.githubusercontent.com/simple-icons/simple-icons/develop/icons/<slug>.svg) is glyph-only and monochrome — use it only as a last resort, and recolor the <path> with fill="#<brandcolor>".
  4. Ask the user for a transparent SVG/PNG wordmark. Prefer asking over shipping a mark-only or white-background logo.

Cache any clean fetched logo to .claude/commands/event-meta-image/assets/logos/<name>.{svg,png} and add a row to references/logos.md. Pass logo paths (absolute, or relative to the config file) in the config; "pulumi" is the one special name.

[Step 5/6] Options

Default every option and move on. Only reach for AskUserQuestion per the interaction mode (and never in unattended mode); never re-confirm what $ARGUMENTS or frontmatter already answered.

  • overline — default event_type (e.g. WORKSHOP, WEBINAR, PANEL); honor an override if $ARGUMENTS gives one.
  • secondary title — hidden. Set it only if the user explicitly asks for a small line above the title.
  • which people / logos to include and their order — when there are more than 5 people, pick the 5 to show; order logos Pulumi-first.

Never add a button. showButton stays false unless the user's prompt explicitly asks for one (e.g. "with a Register button", "add a CTA", "include a sign-up button"). Do not offer it, do not ask about it, and never enable it in unattended mode. Only when explicitly requested, set showButton: true and buttonText accordingly.

[Step 6/6] Render the five sizes

Write one config JSON (e.g. .context/event-images/<slug>/config.json), then loop the renderer over the five sizes.

Config schema (all optional except title):

{
  "overline": "WORKSHOP",
  "title": "Advanced CI/CD for AWS using Pulumi and GitHub Actions",
  "secondaryTitle": "",
  "additionalText": "With Jane Doe - Staff Engineer, Acme and John Roe",
  "showButton": false,
  "buttonText": "Register",         // opt-in only — see below; leave showButton false unless explicitly requested
  "speakers": ["/images/people/jane.jpg", ".context/event-images/<slug>/john-roe.jpg"],
  "logos": ["pulumi", ".claude/commands/event-meta-image/assets/logos/microsoft.svg"]
}
  • additionalText is the literal "With …" line. Compose it yourself: one presenter → With {name} - {role}; many → names only, Oxford comma (With A, B, and C). (Omit to let the CLI derive it from a presenters: [{name, role}] array instead.)
  • showButton is off unless the user explicitly asked for a button (see Step 5). Omit it or leave false in every default and unattended run.
  • speakers / logos resolve as: data: URI → path relative to the config file → repo path (/images/…, static/, assets/fingerprinted/, static/logos/**) → absolute path. Unresolved entries are skipped with a warning.

Render loop — same config, five sizes:

| size | event-bound filename | wired to | |------|----------------------|----------| | 1200x628 | meta.png | meta_image (primary OG + twitter:image) | | 628x628 | meta-square.png | meta_image_square (second og:image) | | 1200x675 | meta-landscape-tall.png | — (manual social) | | 1080x1080| meta-square-large.png | — (manual social) | | 540x960 | meta-portrait.png | — (manual social) |

CFG=.context/event-images/<slug>/config.json
DEST=content/events/<slug>          # event-bound; standalone → .context/event-images/<slug>
node scripts/meta-images/render-event.mjs --config "$CFG" --size 1200x628  --out "$DEST/meta.png"
node scripts/meta-images/render-event.mjs --config "$CFG" --size 628x628   --out "$DEST/meta-square.png"
node scripts/meta-images/render-event.mjs --config "$CFG" --size 1200x675  --out "$DEST/meta-landscape-tall.png"
node scripts/meta-images/render-event.mjs --config "$CFG" --size 1080x1080 --out "$DEST/meta-square-large.png"
node scripts/meta-images/render-event.mjs --config "$CFG" --size 540x960   --out "$DEST/meta-portrait.png"

Event-bound writes (only)

After rendering into content/events/<slug>/, set frontmatter:

meta_image: /events/<slug>/meta.png
meta_image_square: /events/<slug>/meta-square.png

(Optionally set overline: if you customized it.) The three extra sizes are not wired to meta tags — they're for manual social use. Setting meta_image makes the build skip the auto card for this page (the override wins; see layouts/partials/meta-image-url.html).

Standalone writes (only)

Write all five PNGs to the chosen output dir and report their paths. No frontmatter, no bundle.

Wrap-up

Report: mode; the event (event-bound); overline + title used; people shown (and where each photo came from); logos placed; the five output paths; and for event-bound, the frontmatter that was set. Always remind the user to preview the PNGs (and, event-bound, the live page) before committing. Preview the landscape and square first — those are the ones that ship as og:image.

Error handling

  • render-event.mjs prints warning: could not resolve … for any photo/logo it couldn't find — fix the path or re-resolve via the ladder.
  • "Offset is outside the bounds of the DataView" or a decode error → the image is corrupt/truncated; re-fetch it. (Mislabeled extensions like a PNG named .jpg are handled automatically — the renderer sniffs the real type.)
  • No title → ask the user. Long titles auto-shrink to fit; you don't need to trim them.
  • Don't generate or AI-synthesize brand imagery or the Pulumi mascot; use the bundled logo and approved assets only.

References

  • references/logos.md — cached company-logo inventory + the resolution ladder.
  • Renderer internals / layout: scripts/meta-images/events.mjs. Shared CLI: scripts/meta-images/render-event.mjs.

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/generate-pulumi-event-social-cards-ms8gxn3n/download' --output '.skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n/SKILL.md.part' && mv '.skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n/SKILL.md.part' '.skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n/SKILL.md' && test -s '.skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/event-meta-image' && cp -R '.skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n' '.claude/skills/event-meta-image'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n'

openskills read 'generate-pulumi-event-social-cards-ms8gxn3n'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/generate-pulumi-event-social-cards-ms8gxn3n/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills