Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate, revise, translate, and manage App Store marketing screenshots and app icon candidates through the hosted Shots MCP tools. Use for App Store screenshot creation, app icon generation, ASO screenshot strategy, screenshot revisions, localization, App Store listing scraping, and public App Store screenshot inspiration. Do not use for generic image generation.
.claude/skills/hashgraph-online-shots/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 463% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 37% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 57% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 22% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 155% | 0% |
Shots runs through hosted MCP tools. Durable state lives in Convex, generated screenshots and icons are uploaded to the Shots CDN, and completed jobs return CDN URLs plus stable ids.
The hosted MCP server instructions are the source of truth for tool names, arguments, billing recovery, polling cadence, app setup, and review URLs. Use help.schema, help.examples, and help.search before guessing command args.
| Intent | Use | | --- | --- | | Create screenshots | Resolve the app, gather references, use reference/create.md, then call generate_screenshot once per approved panel. | | Use another app as inspiration | Treat the other App Store URL as public gallery inspiration, not the user's app. Use gallery.ensure_app / gallery.get_app, then pass galleryInspirationScreenshotId. | | App icons | Use reference/icons.md, start with generate_icon_moodboard, then generate individual finals with generate_icon. | | Revise | For small targeted changes to an existing screenshot, use screenshots.revise with precise feedback about what changes and what stays fixed. | | Translate/localize | Use screenshots.translate; preserve the campaign system unless the user asks for locale-specific repositioning. | | Promote listing screenshots | Use screenshots.promote / screenshots.unpromote with only the screenshot mediaId; Shots derives app, locale, and platform from the media record. | | Listing/ASO copy | Use reference/strategy.md; save durable listing copy with apps.update_listing and research with apps.update_research. | | Account or billing | Use usage.get or billing; always ask before spending money. |
only tool documentation; never infer the target app identity from them.
Store URL if published, and target platform before calling apps.upsert or apps.import.
user, but do not guess when ambiguity changes the product or creative direction. Ask for missing positioning, audience, real UI truth, style preferences, or approval when needed.
uploaded app_screenshot, a reference image showing actual app UI, or scraped App Store screenshots. An icon, palette, or written description alone is not enough.
screenshots/assets in the repo, upload them first and use the returned media IDs. Do not put local paths in the prompt and assume the image model can see them.
available media, choose the exact references for each panel, pass them as referenceMediaIds, and describe each reference's job in the prompt. Do not rely on Studio/backend default reference selection for quality; that fallback exists for non-agent UI flows.
fixtures, docs, and screenshots. Save durable findings to the app record so Studio and future sessions share the same context.
screenshot and wait for approval or edits.
"try a new style" or "different style." Use gallery inspiration only when the user names/provides an inspiration source or explicitly approves the exact gallery app/screenshot in the plan. Gallery-inspiration generation should stay on medium quality.
style preferences or propose 2-4 concrete directions and get approval before generating.
a new campaign plan and use screenshots.revise instead.
Use media.import_url for HTTPS images. For local files, use the bundled helper:
bashnode <plugin-root>/scripts/upload-asset.mjs --file ./path/to/image.png --app-id <appId> --kind reference
Resolve <plugin-root> as the plugin root directory. From this SKILL.md, the helper is at ../../scripts/upload-asset.mjs.
Useful kind values:
app_screenshot for real product UIicon for the app iconinspo for user-provided mood referencesreference for general brand or product referencesThe helper calls POST https://shots.run/api/upload by default. The endpoint is intentionally open and unauthenticated; it only needs appId and enforces server-side upload safety checks. Do not skip local uploads because R2 or cloud credentials are unavailable.
If an upload fails, stop and call feedback.report with category: "bug", the HTTP status or error message in details, and relatedTool: "upload-asset" plus the appId. Do not generate while treating a required local reference as unavailable unless the user explicitly approves continuing without it.
If the user gives an App Store URL or app id, prefer apps.import; it imports the public icon and saves it as an app icon asset.
If there is no App Store URL, inspect the local app repo and upload the best source icon with --kind icon:
*.xcassets/AppIcon.appiconset/Contents.json, then referenced PNGsapp.json, app.config.*, expo.icon, ios.icon, android.iconmipmap-* assetspublic/manifest.json, public/icon.png, faviconsand a specific UI moment that proves it.
states, and emotional payload.
actually needs.
continuity when they are relevant. Do not pass recent unpromoted generations as references unless the user selects them or asks to continue from them.
borders, or fictional product UI.
Use feedback.report to notify the Shots team about product feedback, feature ideas, unsupported workflows, confusing behavior, user frustration or delight, repeated generation or revision quality issues, and tool or infrastructure errors.
Report when:
delightful.
revision to a whole set.
confuses the user.
upload helper, isError: true responses from Shots commands, or other infrastructure failures. Use category: "bug" with severity: "high", include the error message or HTTP status in details, and set relatedTool or relatedCommand to identify the failing operation.
Include any relevant appId, mediaId, jobId, relatedTool, or relatedCommand. Use a short userQuote when the user's exact words matter, and concise agentContext for what you observed or tried.
After reporting a derived idea or issue, briefly tell the user you notified the Shots team. Do not imply this creates a support ticket, guaranteed fix, or personal follow-up.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 6,567 | 15,892 | +142% | 1 | 1 | 0% | 418 | 2,474 | +492% | 0 | 0 | — |
case-02 | fail→fail | 11,872 | 16,221 | +37% | 1 | 1 | 0% | 609 | 2,369 | +289% | 0 | 0 | — |
case-03 | fail→fail | 24,432 | 13,451 | -45% | 1 | 1 | 0% | 3,125 | 2,744 | -12% | 0 | 0 | — |
case-16 | fail→fail | 13,451 | 8,609 | -36% | 1 | 1 | 0% | 1,240 | 2,517 | +103% | 0 | 0 | — |
case-04 | fail→fail | 45,523 | 17,663 | -61% | 1 | 1 | 0% | 8,230 | 2,446 | -70% | 0 | 0 | — |
case-05 | fail→pass | 4,048 | 5,590 | +38% | 1 | 1 | 0% | 553 | 3,116 | +463% | 0 | 0 | — |
case-06 | fail→fail | 13,468 | 14,205 | +5% | 1 | 1 | 0% | 1,165 | 2,702 | +132% | 0 | 0 | — |
case-07 | fail→pass | 11,345 | 3,450 | -70% | 1 | 1 | 0% | 1,919 | 2,635 | +37% | 0 | 0 | — |
case-08 | fail→fail | 5,174 | 8,701 | +68% | 1 | 1 | 0% | 1,017 | 2,660 | +162% | 0 | 0 | — |
case-09 | fail→fail | 21,997 | 6,707 | -70% | 1 | 1 | 0% | 2,638 | 2,279 | -14% | 0 | 0 | — |
case-10 | pass→fail | 6,416 | 11,676 | +82% | 1 | 1 | 0% | 881 | 2,355 | +167% | 0 | 0 | — |
case-11 | fail→fail | 10,097 | 7,578 | -25% | 1 | 1 | 0% | 763 | 2,510 | +229% | 0 | 0 | — |
case-17 | fail→fail | 13,334 | 23,415 | +76% | 1 | 1 | 0% | 1,841 | 2,962 | +61% | 0 | 0 | — |
case-12 | fail→pass | 23,134 | 11,139 | -52% | 1 | 1 | 0% | 2,413 | 3,792 | +57% | 0 | 0 | — |
case-13 | fail→pass | 18,279 | 2,854 | -84% | 1 | 1 | 0% | 1,965 | 2,404 | +22% | 0 | 0 | — |
case-14 | fail→pass | 5,895 | 9,544 | +62% | 1 | 1 | 0% | 1,077 | 2,749 | +155% | 0 | 0 | — |
case-15 | fail→fail | 9,630 | 13,461 | +40% | 1 | 1 | 0% | 526 | 2,368 | +350% | 0 | 0 | — |
case-18 | pass→pass | 17,937 | 12,647 | -29% | 1 | 1 | 0% | 1,863 | 3,092 | +66% | 0 | 0 | — |
case-19 | pass→pass | 15,874 | 10,775 | -32% | 1 | 1 | 0% | 1,540 | 3,272 | +112% | 0 | 0 | — |
case-20 | pass→pass | 20,436 | 12,037 | -41% | 1 | 1 | 0% | 1,978 | 2,899 | +47% | 0 | 0 | — |
case-21 | pass→pass | 18,049 | 10,491 | -42% | 1 | 1 | 0% | 1,654 | 2,711 | +64% | 0 | 0 | — |
case-22 | pass→fail | 14,916 | 25,450 | +71% | 1 | 1 | 0% | 1,726 | 2,448 | +42% | 0 | 0 | — |
case-23 | pass→fail | 13,248 | 11,823 | -11% | 1 | 1 | 0% | 1,325 | 2,412 | +82% | 0 | 0 | — |
case-24 | pass→pass | 12,581 | 20,558 | +63% | 1 | 1 | 0% | 3,003 | 4,428 | +47% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted, and 12 counted toward the lift figure. The other 12 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +8 percentage points is the difference between those two pass rates over the 12 comparable cases. 4 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.