skills/share-assets/SKILL.md
--- name: share-assets description: From one source brand mark, generate the full set of identity + share assets (favicon, app icons, PWA manifest, static OG image, Twitter card, dynamic OG route, JSON-LD) and wire them into a TanStack Start app's head + public/. Use when starting a new app or when an existing app's link previews look broken on X / WhatsApp / iMessage / LinkedIn / Slack. category: development argument-hint: [--source <path-to-svg>] [--no-dynamic-og] [--brand-color <hex>] <app-na
npx skillsauth add RonanCodes/ronan-skills skills/share-assetsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Builds out the brand-identity + social-share asset set every shipped app needs but every new repo forgets. One source SVG mark in, the full deliverable set out:
public/favicon.ico (multi-resolution, 16/32/48)public/favicon.svg (vector, theme-aware via prefers-color-scheme)public/icons/icon.svg (master)public/icons/icon-192.png, icon-512.png, icon-maskable-512.png (PWA manifest)public/icons/apple-touch-icon.png (180px)public/manifest.webmanifest (with the icons array wired)public/og-static.png (1200x630 fallback for any URL)src/routes/api/og.ts (dynamic per-URL OG via Satori on Workers; delegates to /ro:og-image-dynamic if not yet wired)src/routes/__root.tsx: og:*, twitter:*, link rel="icon", link rel="manifest", link rel="apple-touch-icon", theme-color, JSON-LD application schema--audit-only to report what's missing without writing.Every link a customer or investor pastes into X / Reddit / LinkedIn / WhatsApp / iMessage / Slack triggers an OpenGraph fetch. The default "no preview" rendering looks broken; a wrong-aspect-ratio static image looks lazy; a per-URL dynamic image looks polished. Same with the favicon: a default Vite/Next.js placeholder favicon is the visual equivalent of a "Hello World" landing page.
Fix once, fix permanently, automate the rest.
/ro:share-assets --source brand/dataforce-mark.svg dataforce
/ro:share-assets --source ./logo.svg --brand-color "#10b981" my-app
/ro:share-assets --no-dynamic-og my-app # skip the /api/og route, static-only
/ro:share-assets --audit-only # report gaps; don't write
If --source is omitted, the skill prompts (AskUserQuestion) for an existing brand mark path or offers to call /ro:generate-image to create one from a brief.
Verify the source SVG:
If the source fails, prompt for a different path or call /ro:generate-image with a "brand mark, square, single-colour, modern" prompt.
Use ImageMagick (magick on macOS / convert on Linux) or sharp (Node) to derive PNGs at 16, 32, 48, 180, 192, 512. Bundle the 16/32/48 PNGs into a multi-resolution favicon.ico:
magick -density 300 -background none source.svg \
-define icon:auto-resize=16,32,48 public/favicon.ico
magick -density 300 -background none source.svg -resize 180x180 public/icons/apple-touch-icon.png
magick -density 300 -background none source.svg -resize 192x192 public/icons/icon-192.png
magick -density 300 -background none source.svg -resize 512x512 public/icons/icon-512.png
# Maskable: pad with 20% safe zone on all sides
magick -density 300 -background "#<brand-color>" source.svg \
-resize 320x320 -gravity center -extent 512x512 \
public/icons/icon-maskable-512.png
cp source.svg public/favicon.svg
cp source.svg public/icons/icon.svg
Theme-aware favicon (optional but nice): the SVG can use <style> with prefers-color-scheme: dark so the favicon adapts to the user's OS. Document this in the source.
public/og-static.png is the fallback when /api/og isn't reachable or for very old crawlers. 1200x630, brand-coloured, with the app name and a one-line tagline.
If the user already has a designed marketing hero, derive from that. Otherwise compose a minimal layout:
magick -size 1200x630 \
-background "#<brand-color>" \
-fill white \
-font Inter-Bold \
-pointsize 92 \
-gravity center \
-annotate 0 "<App Name>" \
public/og-static.png
manifest.webmanifest{
"name": "<App Name>",
"short_name": "<Short>",
"description": "<one-line description>",
"start_url": "/?source=pwa",
"scope": "/",
"display": "standalone",
"display_override": ["standalone", "browser"],
"background_color": "#ffffff",
"theme_color": "#<brand-color>",
"orientation": "portrait-primary",
"categories": ["productivity"],
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
{ "src": "/icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
In src/routes/__root.tsx head function:
const SITE_ORIGIN = 'https://<your-domain>'
const TITLE = '<App Name>: <one-line tagline>'
const DESCRIPTION = '<150-character description>'
const OG_IMAGE = `${SITE_ORIGIN}/api/og` // dynamic; falls back to /og-static.png if route unreachable
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ title: TITLE },
{ name: 'description', content: DESCRIPTION },
{ name: 'theme-color', content: '#<brand-color>' },
// OpenGraph
{ property: 'og:type', content: 'website' },
{ property: 'og:site_name', content: '<App Name>' },
{ property: 'og:title', content: TITLE },
{ property: 'og:description', content: DESCRIPTION },
{ property: 'og:url', content: SITE_ORIGIN },
{ property: 'og:image', content: OG_IMAGE },
{ property: 'og:image:width', content: '1200' },
{ property: 'og:image:height', content: '630' },
// Twitter / X
{ name: 'twitter:card', content: 'summary_large_image' },
{ name: 'twitter:title', content: TITLE },
{ name: 'twitter:description', content: DESCRIPTION },
{ name: 'twitter:image', content: OG_IMAGE },
],
links: [
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'apple-touch-icon', href: '/icons/apple-touch-icon.png' },
{ rel: 'manifest', href: '/manifest.webmanifest' },
],
scripts: [
{
type: 'application/ld+json',
children: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'WebApplication',
name: '<App Name>',
url: SITE_ORIGIN,
description: DESCRIPTION,
applicationCategory: '<Productivity / Game / Tool>',
operatingSystem: 'Web',
}),
},
],
})
Delegates to the /ro:og-image-dynamic skill for the implementation. The skill scaffolds src/routes/api/og.ts using Satori + @resvg/resvg-wasm on Workers.
When this skill (/ro:share-assets) runs and the OG route doesn't exist yet, it invokes /ro:og-image-dynamic --runtime cf automatically. Pass --no-dynamic-og to skip and stick with the static og-static.png.
After scaffolding, the skill runs:
curl -sI https://<deployed-url>/favicon.ico # 200 image/x-icon
curl -sI https://<deployed-url>/favicon.svg # 200 image/svg+xml
curl -sI https://<deployed-url>/manifest.webmanifest # 200 application/manifest+json
curl -sI https://<deployed-url>/og-static.png # 200 image/png, ~30-80kb
curl -sI https://<deployed-url>/api/og # 200 image/png if dynamic wired
And renders the head + checks:
curl -s https://<deployed-url> | grep -E 'og:image|twitter:card|rel="(icon|manifest|apple-touch)"'
Then opens https://www.opengraph.xyz/?url=https://<deployed-url> and https://cards-dev.twitter.com/validator (if it still exists) to visually verify the share preview.
--audit-only reports what's missing without writing:
✓ favicon.svg
✓ favicon.ico
✗ apple-touch-icon.png (missing)
✓ manifest.webmanifest
✗ icon-maskable-512.png (missing; manifest references it)
✗ /api/og route (no dynamic OG)
✓ og-static.png
✓ Head meta tags (og:*, twitter:*)
✗ JSON-LD application schema (missing in __root.tsx)
Returns exit code 0 if all present, 1 if any missing. Useful in CI as a launch-readiness gate.
/ro:generate-image or use an existing source).useHead() pattern in route components, scoped to each story)./ro:seo-launch-ready (which handles sitemap, robots, JSON-LD basics, canonical URLs)./ro:pwa-install (which handles service worker + install prompt).[[ideal-tech-setup]] § Greenfield Spec Baseline — share-assets is item #11/ro:og-image-dynamic — wires the per-URL dynamic OG route/ro:seo-launch-ready — sitemap, robots, JSON-LD, canonical URLs/ro:pwa-install — manifest + service worker for installability/ro:app-polish — 10-point launch-readiness audit (this skill is one of the points)development
--- name: worktree description: Coordinate multiple agents on one repo via a worktree-lock pool, so two agents never clobber each other's working tree. Acquire the first free slot (main, then beta/gamma… worktrees, created on demand), work there on your own branch, release when you've pushed. Use before modifying any repo that might be in use by another agent (factory, dataforce, etc.), or whenever you're told a repo is being worked on. Backed by `ro worktree`. category: development argument-hin
testing
--- name: ship description: Ship a feature branch the local-CI-first way — run the full local gate, push, open a PR, squash-merge, then deploy, without waiting on GitHub Actions. Use when a branch is ready for main and you want it merged and deployed now. Reads CI policy from `ro ci` (default skips remote CI because GitHub Actions billing keeps hitting limits). Sibling to /ro:gh-ship (waits on GitHub checks) and /ro:cf-ship (the deploy half). Triggers on "ship it", "ship this", "merge and deploy
testing
--- name: setup-logging description: Set up (or audit) the observability stack in a TanStack Start + Cloudflare Workers app so it is "diagnosable by default" — structured logging (logtape) with a request context carrying trace_id + userId + tenant/orgId, a trace_id propagated FE→BE→logs→Sentry→PostHog, Cloudflare Workers observability enabled, and Sentry + PostHog wired. Two modes: `setup` (wire it into an app) and `audit` (check an existing app + report gaps). Use when scaffolding a new app, wh
development
Manage credentials INSIDE the active ~/.claude/.env file — read which token/account to use for a given app (Simplicity vs Dataforce vs Ronan-personal), add or update a secret WITHOUT it passing through the chat (an interactive Terminal window prompts for it), and track secrets that were exposed in a transcript so they get rotated. Sibling to /ro:context (which switches WHICH env file is active). Use when the user wants to add an API key/token/secret, asks "which credential do I use for X", needs the env organized/labelled, or a secret was pasted into the chat and should be rotated.