Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Draft and polish tweets with optional code cards, stat cards, or screenshot wraps. Use when the user wants a tweet or X post.
user_invocable
true
Tweet Skill
Help the user draft, refine, and finalize tweets with compelling visuals. Automatically selects the right visual format based on content.
Gotchas
Character count is tricky -- URLs always count as 23 chars (t.co wrapping). Emojis count as 2. Newlines count as 1. Don't trust naive length checks.
Twitter crops images at 16:9 in timeline -- all templates output 1600x900 for this reason. Non-16:9 images get cropped unpredictably.
Don't over-polish -- tweets that sound too perfect feel like marketing. Preserve the user's natural voice, just fix obvious issues.
Thread vs single tweet -- if the user hasn't asked for a thread, don't suggest one. Single tweets perform better unless there's genuinely too much for 280 chars.
Screenshot dependencies -- sharp and @resvg/resvg-js need to be installed. If they're not global, install to a temp dir, don't pollute the project.
Hot takes can backfire -- always flag the hot take variation as risky. The user should consciously opt in, not accidentally post something inflammatory.
Always suggest a visual -- text-only dev tweets get buried by the algorithm. Every finalized tweet should have a visual recommendation, even if the user didn't ask for one.
Strip AI tells -- before finalizing, run copy through /humanize-writing. No em-dashes, no "it's not X, it's Y", name specific tools/numbers over vague claims. AI-sounding tweets read as marketing and get ignored.
Data Storage
Track tweet history for voice consistency:
# After finalizing a tweet, log itecho"$(date -I) | VISUAL_TYPE | FINAL_TWEET_TEXT" >> "${CLAUDE_PLUGIN_DATA}/tweet-history.log"
On subsequent runs, read recent history to maintain consistent voice and avoid repeating hooks/angles.
Input
$ARGUMENTS may contain:
Raw tweet text to refine
A file path to a screenshot to wrap
A code snippet to visualize
Release notes or changelog to turn into a launch tweet
Nothing (interactive mode)
Step 1: Analyze Input & Select Visual Strategy
Determine what the user provided and select the appropriate visual:
Input type
Visual template
When to use
Screenshot/image path
wrap-screenshot.mjs
User has an existing screenshot to polish
Code snippet or mentions code
code-card.mjs
Showing a specific API, config, or code example
Release/launch with metrics
stat-card.mjs
GitHub stars, downloads, benchmarks, version milestones
General text, no obvious visual
Suggest one
Ask: "What's the most visual part of this?"
If the input clearly maps to one visual, proceed directly. If more than one fits, briefly suggest the best fit and why.
Theme Selection
All templates share 6 color themes. Pick based on context:
Theme
Vibe
Best for
midnight
Deep blue, electric accents
General dev content, default choice
ocean
Teal/cyan, calm
Nuxt/Vue ecosystem (matches brand colors)
sunset
Warm orange/purple
Creative tools, design content
forest
Green, natural
Performance, sustainability, growth metrics
lavender
Purple, elegant
TypeScript, DX tooling
ember
Red, intense
Breaking changes, critical fixes, hot takes
Default to ocean for Nuxt ecosystem content, midnight for everything else.
Chrome Selection (wrap-screenshot only)
Chrome
Look
Best for
terminal
macOS terminal with traffic lights + monospace title
CLI tools, terminal output
browser
Browser-style with URL bar
Web apps, UI screenshots
minimal
Thin top bar, no buttons
Clean look, design screenshots
none
Just the image on background
When the content speaks for itself
Step 2: Tweet Text Iteration
Initial Review
Read the draft and identify:
Character count (280 limit, flag if over)
Weak opening (first 5 words matter most in timeline)
Missing context that makes the tweet confusing to outsiders
Corporate speak ("excited to announce", "thrilled to share")
Starting with "I" (reframe to lead with value)
Generate Variations
Produce 3 variations plus the fixed original:
Fixed original -- same voice/intent, fix obvious issues
Value-first -- reframe around what the reader gets. "Free tool that does X" over "I built X"
Show-don't-tell -- lead with the visual/demo. "Watch:" or "Before/after:" framing
Provocative -- stronger opinion or contrarian angle (flag as higher risk)
For each variation show:
The tweet text
Character count
What changed and why
Which visual template + theme pairs best with this angle
Rules for Good Tweets
No hashtags unless the user explicitly includes them
No emojis unless the user's draft already uses them
Match the user's voice
If referencing code/tech, be specific not vague
Front-load the hook in the first line
Iterate
After showing variations, ask which direction they prefer or if they want to mix elements. Keep iterating until they're happy.
Step 3: Generate Visual
Based on the selected template, create a temporary script in scratchpad.
3a. Screenshot Wrapping (wrap-screenshot.mjs)
Use the Read tool to view the screenshot first. Then customize:
INPUT_PATH -- the screenshot file path
OUTPUT_PATH -- same directory, append -twitter before extension
TITLE -- contextual title based on content:
Terminal: claude — ~/project or the command shown
Browser: URL or page title
Generic: filename or brief description
THEME -- selected theme
CHROME -- selected chrome style
3b. Code Card (code-card.mjs)
Extract the most interesting code snippet (max ~20 lines, the card clips overflow). Customize:
CODE -- the code to display (clean it up: remove irrelevant imports, focus on the "aha" moment)
LANGUAGE -- for the title bar label
TITLE -- optional override (e.g., "nuxt.config.ts" or "Usage")
THEME -- selected theme
OUTPUT_PATH -- descriptive name
Tips for good code cards:
Show the simplest usage, not the full API
5-15 lines is ideal; under 5 feels empty, over 20 gets cramped
Include a comment that explains what's happening if the code isn't self-evident
Remove type annotations if they add noise without clarity
3c. Stat Card (stat-card.mjs)
For milestones, releases with impressive numbers, benchmarks. Customize:
HEADLINE -- project name + version or milestone label
SUBHEADLINE -- one-line description
STATS -- array of { label, value } objects (2-4 stats, 3 is ideal)
THEME -- selected theme
OUTPUT_PATH -- descriptive name
Tips for good stat cards:
Round numbers up ("1,200+" not "1,187")
Use human-readable units ("45k" not "44,892")
Lead with the most impressive stat
Pair with a tweet that gives context the card doesn't
3d. Run the Script
node /path/to/scratchpad/template.mjs
Dependencies (sharp, @resvg/resvg-js) should be available globally or in the project. If not, install to scratchpad:
cd /path/to/scratchpad && pnpm init && pnpm add sharp @resvg/resvg-js
3e. Verify Output
Read the output image to verify it looks correct. Show the user the result. If something looks off (text overflow, bad contrast, wrong theme), fix and re-run.
Step 4: Final Output
Present:
The final tweet text with character count
The generated visual (show the image)
Suggested accounts to tag (max 2, relevant to content)