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.
Use it when you need to create or enrich a Pulumi event's social card with speakers or logos that frontmatter does not cover.
Avoid it outside the Pulumi repository and shared renderer setup, or when the automatic build-time card is already sufficient.
Security analysis
SafeThe 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 madeExamples
/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./event-meta-image -y for the upcoming workshop event: derive the presenters and partner logos automatically and skip anything you can't resolve.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$ARGUMENTSnames an event path/slug, whengit statusshows 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
$ARGUMENTScontains-y,--yes,--non-interactive,--no-input,no questions, or "just do it" (and automatically whenever you're running non-interactively, e.g. underclaude -por the eval, whereAskUserQuestioncan't be answered). Skip everyAskUserQuestion. Derive all inputs automatically:- overline =
event_type; title = frontmattertitle; speakers = frontmatterpresenters[].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.
- overline =
- Default — ask only when needed. Same automatic derivation and search as unattended mode, but fall back to a single targeted
AskUserQuestiononly when an asset can't be resolved, or an input is genuinely ambiguous (multiple candidate events, missingtitle, 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)
- If
$ARGUMENTScontains an event path or slug, usecontent/events/<slug>/index.md. - Otherwise run
git status/git diff --name-onlyfor changedcontent/events/*/index.md. - 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. Eachphoto(e.g./images/people/<x>.jpgor/images/team/<x>.jpg) resolves understatic/.
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):
- An existing
presenters[].photo(already have it). static/images/team/<kebab-name>.{jpg,jpeg,png}.static/images/people/<kebab-name>.{jpg,jpeg,png}.- Web:
WebSearchfor a headshot / company bio photo, thenWebFetchthe image. LinkedIn images are usually auth-gated — expect this to fail and fall through. - 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 byfileasRGBwith no alpha is opaque;RGBAor a colormap withtRNSmay be transparent — check the render).
Resolve each via this ladder, stopping at the first hit that satisfies both rules:
.claude/commands/event-meta-image/assets/logos/<name>.svg(cached — seereferences/logos.md).static/logos/**(the site's own brand assets — often clean SVG wordmarks).- Web —
WebSearch"<company> logo wordmark SVG transparent", thenWebFetch/curlthe 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>withfill="#<brandcolor>". - 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$ARGUMENTSgives 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"]
}
additionalTextis 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 apresenters: [{name, role}]array instead.)showButtonis off unless the user explicitly asked for a button (see Step 5). Omit it or leavefalsein every default and unattended run.speakers/logosresolve 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.mjsprintswarning: 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
.jpgare 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 filesThis 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.mdFrom 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 installationopenskills 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 syncManual 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.
Writing Fragments
Content
Mine raw writing fragments through a relentless interview, without imposing structure. Capture ideas, quotes, and leading words for later use.
UI Copywriting & Auditing
Content
Write or audit user-facing UI copy including buttons, errors, empty states, and tooltips across the monorepo, following the design system source of truth.
Music MIDI Extraction
Content
Extracts the MIDI or score from an existing music production via the CogMusic MCP tool and confirms availability.