From viralman
Drives end-to-end flow turning one-line intents into platform-tuned, non-AI-feeling promo posts for Reddit, X/Twitter, or LinkedIn, publishing after confirmation.
npx claudepluginhub art8engine/viralman --plugin viralmanThis skill uses the workspace's default tool permissions.
This skill is the spine of the viralman plugin. The `/viral` slash command and any natural-language trigger ("바이럴해줘", "make a promo post for…") route here. The skill owns the multi-step flow; the agents do the writing and review; the scripts do the posting.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Explores codebases via GitNexus: discover repos, query execution flows, trace processes, inspect symbol callers/callees, and review architecture.
Share bugs, ideas, or general feedback.
This skill is the spine of the viralman plugin. The /viral slash command and any natural-language trigger ("바이럴해줘", "make a promo post for…") route here. The skill owns the multi-step flow; the agents do the writing and review; the scripts do the posting.
Auto-trigger when the user's message contains any of:
These have no explicit platform, so Step 0 below MUST run the 3-way router (X / Reddit / Gitmail):
바이럴해줘, 바이럴 해줘, 홍보글 작성해줘, 홍보 글 써줘, 홍보해줘, 이거 홍보make this go viral, write a promo post, write a launch post, promote this내가 한 거 자랑해줘, 이번 출시 글 써줘The trigger names the channel(s) directly — Step 0 must be skipped and Targets set to exactly the named platform(s):
x에 올려줘, x에 써줘, x에 포스팅, 트위터에 올려줘, 트윗 써줘, tweet about this, post this to x/twitter, write an X threadreddit에 올려줘, reddit에 써줘, 레딧에 올려줘, r/<sub>에 올려줘, post this to reddit, draft a reddit postx랑 reddit에 올려줘, x하고 reddit에 올려줘, 트위터랑 레딧에 올려줘, post this to x and redditlinkedin에 올려줘, LinkedIn 공지 써줘, post this to linkedinWhen parsing, normalize aliases: 트위터 == 트윗 == x == twitter, 레딧 == reddit. A bare reference like "스레드 만들어줘" without a platform name still counts as generic — run the router.
If the user typed /viral, follow commands/viral.md for argument parsing first.
Run only if no platform was named in the trigger (i.e., generic intent). Skip entirely when the trigger already named X / Reddit / X+Reddit / LinkedIn.
Use AskUserQuestion (multiSelect=true) with these three options, in this order:
| label | header | description |
|---|---|---|
| X (Twitter) | X | 단일 트윗 / 짧은 스레드. 빠른 도달, 캐주얼 톤. |
| 특정 서브레딧에 1건. 길이·논점 자유, 서브레딧 사양에 맞춰 작성. | ||
| Gitmail (cold email) | Gitmail | 비슷한 레포 스타거에게 개인화된 메일 발송. gitmail 스킬로 분기. |
Then:
Targets accordingly and continue with Step 1 of THIS skill (viral).gitmail skill: stop the viral flow, follow skills/gitmail/SKILL.md from its Step 1. Do not draft a Reddit/X post in this branch.LinkedIn is intentionally not in the 3-way router (its CLI helper still works via the explicit trigger above), to keep the default surface focused on the user's stated three channels.
/viral slash-command invocation, default is reddit, x, linkedin (per commands/viral.md). Reddit requires a specific subreddit — if not given, ask once.growth-story | casual-hype | show-and-tell | contrarian-take. Pick a default from the intent's tone; confirm if borderline.Print a one-block plan: target platforms, mode, subreddit (if any), and the anchor(s) you'll use. Wait for the user to nudge or say "go".
If the user has explicitly said "I'll write the body myself" / "직접 입력할게" / pasted a finished draft, follow skills/copy-prep/SKILL.md §Manual override pattern instead — skip the writer agent and pass the user-provided body straight to the post script in Step 5. Otherwise:
Spawn the viral-writer agent once per platform in parallel. Pass it:
voice/modes/<mode>.md.voice/platform-norms.md.voice/reference-corpus/.voice/ai-tells.md (so the writer can avoid them up front).Each draft is returned as plain text with no surrounding commentary.
Spawn ai-tell-sniffer on each draft separately. The sniffer:
voice/ai-tells.md.Authoring (writer) and review (sniffer) MUST be different agent invocations. Do not collapse them.
Print all drafts side by side (or stacked if width is tight). For each, show:
Then ask the user, per-platform: [post] [edit] [regenerate] [skip].
Use AskUserQuestion for this — and surface, in the option descriptions, that the user can attach free-form instructions in the same step:
Post — send as-is via the matching scripts/post_*.py.Edit — describe the specific changes you want (tone, hook line, length cap, swap a phrase, etc.); the skill will surgically rewrite using only those instructions, then re-run the sniffer.Regenerate — optionally describe what to change about the framing/anchors; the skill will spawn a fresh viral-writer call with those instructions folded into the prompt, then re-run the sniffer.Skip — drop this platform.When the user's response includes free-form text alongside an option (either via the option's notes annotation or via the Other free-text fallback), treat that text as additional instructions:
Edit: pass the notes verbatim to the writer as a "minimal-change rewrite" directive — keep the existing draft as the base, change only what the notes call out, then re-sniff.Regenerate: prepend the notes to the writer's INTENT block as a USER OVERRIDE section so they take precedence over earlier defaults, then re-sniff.When Regenerate is picked without notes, treat all prior user-supplied constraints from the same flow (added pillars, must-mention items, banned phrasings, length caps, language) as still binding. A note-less Regenerate means "try a different angle within the same constraints", not "reset everything". Carry every explicit prior constraint into the new writer prompt verbatim.
Never silently drop user notes. If the notes contradict a hard rule (e.g. "make it 500 chars" on X), apply the rule, surface the conflict in one line, and proceed with the rule honored.
If the user asked for --yes, skip this step unless there are unresolved flags. Refuse to auto-post a flagged draft.
For each platform with a post decision:
| Platform | Script | Behavior |
|---|---|---|
viralman post-reddit --subreddit <sub> --title <title> --body - | Reads body from stdin, posts via PRAW, returns the permalink. | |
viralman post-linkedin --body - | Posts via UGC Posts API, returns the post URN/URL. | |
| X (Twitter) | viralman post-twitter --body - | If TWITTER_BEARER is set, posts via API; otherwise opens a https://twitter.com/intent/tweet?text=… URL via open and prints the URL for the user to one-click send. |
Capture each script's stdout — the URL/URN — and print it. If a script exits non-zero, do not retry blindly: print the error, drop that platform, continue with others.
Append one JSON line per posted (or attempted) item to ~/.viralman/posts.jsonl:
{"ts":"2026-04-25T16:30:00Z","platform":"reddit","mode":"growth-story","subreddit":"programming","url":"https://reddit.com/...","chars":612,"flags":[]}
This is the user's only audit trail. Never write credentials, the original intent, or the full body into the log — just metadata + URL.
~/.viralman/.env. Only the scripts/post_*.py helpers do.post decision in the same turn.