.agents/skills/forbotsake-create/SKILL.md
Writes actual marketing content -- X threads, blog posts, emails, LinkedIn posts, and more. Reads your strategy.md and content-calendar.md, asks what piece to create, then generates channel-tailored content using your positioning, ICP, and messaging pillars. Use when: "write a thread", "create content", "write a blog post", "draft an email", "LinkedIn post", "write marketing copy", "create a tweet", "help me write", "content piece". Proactively invoke when the user asks to write or draft any marketing content and a strategy.md exists.
npx skillsauth add forbotsake/forbotsake forbotsake-createInstall 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.
Write the actual content. Not templates. Not frameworks. Real content ready to publish.
FORBOTSAKE_HOME="${FORBOTSAKE_HOME:-$HOME/.forbotsake}"
mkdir -p "$FORBOTSAKE_HOME"
# Discover forbotsake install directory
_FBS_ROOT=""
for _FBS_CANDIDATE in "$HOME/.codex/skills/forbotsake" "$HOME/.agents/skills/forbotsake"; do
[ -d "$_FBS_CANDIDATE" ] && _FBS_ROOT="$_FBS_CANDIDATE" && break
done
if [ -z "$_FBS_ROOT" ]; then
echo "WARNING: forbotsake not found. Install: bash <(curl -fsSL https://raw.githubusercontent.com/forbotsake/forbotsake/main/bin/install.sh)"
fi
# Check for updates
_UPD=""
[ -n "$_FBS_ROOT" ] && [ -x "$_FBS_ROOT/bin/forbotsake-update-check" ] && _UPD=$("$_FBS_ROOT/bin/forbotsake-update-check" 2>/dev/null || true)
[ -n "$_UPD" ] && echo "$_UPD" || true
_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
echo "BRANCH: $_BRANCH"
# Check for strategy.md
if [ -f strategy.md ]; then
echo "STRATEGY_EXISTS: yes"
echo "STRATEGY_FILE: strategy.md"
elif [ -f forbotsake-strategy.md ]; then
echo "STRATEGY_EXISTS: yes"
echo "STRATEGY_FILE: forbotsake-strategy.md"
else
echo "STRATEGY_EXISTS: no"
fi
# Check for content-calendar.md
if [ -f content-calendar.md ]; then
echo "CALENDAR_EXISTS: yes"
elif [ -f forbotsake-content-calendar.md ]; then
echo "CALENDAR_EXISTS: yes"
echo "CALENDAR_FILE: forbotsake-content-calendar.md"
else
echo "CALENDAR_EXISTS: no"
fi
# Check for existing content directory
if [ -d content ]; then
echo "CONTENT_DIR: exists"
ls -1t content/ 2>/dev/null | head -5
else
echo "CONTENT_DIR: missing"
fi
# Today's date for file naming
echo "TODAY: $(date +%Y-%m-%d)"
# Multi-modal: check for brand.md
if [ -f brand.md ]; then
echo "BRAND_EXISTS: yes"
head -3 brand.md
else
echo "BRAND_EXISTS: no"
fi
# Multi-modal: check for media-providers.md (or auto-detect)
if [ -f media-providers.md ]; then
echo "PROVIDERS_EXISTS: yes"
else
echo "PROVIDERS_EXISTS: no"
# Auto-detect available providers
echo "--- PROVIDER DETECTION ---"
command -v bun >/dev/null 2>&1 && echo "SATORI_AVAILABLE: yes (bun)" || { command -v node >/dev/null 2>&1 && echo "SATORI_AVAILABLE: yes (node)" || echo "SATORI_AVAILABLE: no"; }
echo "CHROME_MCP: check-at-runtime"
[ -n "${NANO_BANANA_API_KEY:-}" ] && echo "NANO_BANANA_API: yes" || echo "NANO_BANANA_API: no"
[ -n "${SEEDANCE_API_KEY:-}" ] && echo "SEEDANCE_API: yes" || echo "SEEDANCE_API: no"
echo "--- END DETECTION ---"
fi
# Orchestrated mode (invoked by forbotsake-go, propagated via file flag)
_ORCH_FILE="${FORBOTSAKE_HOME:-$HOME/.forbotsake}/orchestrated-$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")"
FORBOTSAKE_ORCHESTRATED=$(cat "$_ORCH_FILE" 2>/dev/null || echo 0)
echo "ORCHESTRATED: $FORBOTSAKE_ORCHESTRATED"
# Check for session resume file
_SESSION_FILE="$FORBOTSAKE_HOME/session-create-$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)").json"
if [ -f "$_SESSION_FILE" ]; then
echo "RESUME_AVAILABLE: yes"
echo "SESSION_FILE: $_SESSION_FILE"
else
echo "RESUME_AVAILABLE: no"
fi
If output shows UPGRADE_AVAILABLE <old> <new>: read the forbotsake-upgrade SKILL.md
at $_FBS_ROOT/forbotsake-upgrade/SKILL.md (where _FBS_ROOT is the variable already
resolved in the preamble bash above) and follow the "Inline upgrade flow" section Step 1
only. If Step 1 results in "Yes" or "Always" (proceed with upgrade), continue through
Steps 2-7 of the inline flow. If Step 1 results in "Not now" or "Never" (declined),
skip Steps 2-7 entirely and continue with this skill immediately.
If output shows JUST_UPGRADED <old> <new>: tell user
"Running forbotsake v{new} (just updated from v{old})!" and continue.
If STRATEGY_EXISTS is no:
"No strategy.md found. You need a strategy before creating content -- otherwise you're writing into the void. Run
/forbotsake-marketing-startfirst to define your positioning, ICP, and channels."
Stop here. Do NOT proceed without a strategy.
If CALENDAR_EXISTS is no, note it but proceed:
"No content-calendar.md found. That's OK -- we can create content without a calendar. But if you want a structured plan first, run
/forbotsake-content-plan. Otherwise, let's pick what to write."
If RESUME_AVAILABLE is yes AND ORCHESTRATED is 0: "Found a previous session in progress. Resume where you left off?"
Ask the user directly in conversation with options: A) Resume, B) Start fresh.
If ORCHESTRATED is 1 and RESUME_AVAILABLE is yes: ignore the resume file and start fresh.
If PROVIDERS_EXISTS is no, auto-generate media-providers.md based on detected capabilities:
Read the provider framework:
_SKILL_DIR=$(dirname "$(find $HOME/.codex/skills -path "*/forbotsake-marketing-start/SKILL.md" -type f 2>/dev/null | head -1)")
echo "SKILL_DIR: $_SKILL_DIR"
Read $_SKILL_DIR/../knowledge/frameworks/media-providers.md for the schema.
Write media-providers.md to the project root with detected providers enabled/disabled based on preamble detection output. If no providers are available, write the config with all disabled and fallback: prompt-only. Tell the user: "Created media-providers.md. Visual prompts will be saved for manual generation. Install bun/node for text-cards, or Claude for Chrome extension for AI images."
If PROVIDERS_EXISTS is yes, read it to know which providers are available.
Read strategy.md completely. Extract and internalize:
If brand.md exists, read it and extract:
Check if there are previous reviewer notes from a HARD_FAIL or SOFT_FAIL in
/forbotsake-content-check. If the user is re-creating content after a failed review,
the content file may have reviewer_notes: in its frontmatter.
If the user specified a content file to rewrite, read its frontmatter. If reviewer_notes:
exists, extract the findings and use them as constraints for this creation session:
"I found reviewer notes from a previous review. I'll avoid these issues: {for each reviewer note: dimension and fix suggestion}"
Incorporate these constraints into the content generation. Do not repeat the patterns the reviewer flagged.
If content-calendar.md exists, read it and extract:
Orchestrated mode (ORCHESTRATED is 1): Auto-select the content piece:
Interactive mode (ORCHESTRATED is 0): Follow the normal flow below.
If a content calendar exists and has upcoming slots, present them:
"From your content calendar, here's what's next:
- {Day} - {Channel}: {Content type} about {topic} (Week {N}: {theme})
- {Day} - {Channel}: {Content type} about {topic}
- {Day} - {Channel}: {Content type} about {topic}
Pick one, or tell me what else you want to create."
If no calendar, ask via direct conversation with the user:
"What content piece do you want to create?
A) X/Twitter thread B) X/Twitter single tweet or tweet storm C) Blog post D) LinkedIn post E) Email / newsletter F) Reddit post or comment G) Hacker News Show HN post H) Product Hunt launch copy I) Something else -- describe it"
Session save after answer:
echo '{"phase": "content_choice", "channel": "CHANNEL", "type": "TYPE"}' >> "$_SESSION_FILE"
Follow up via direct conversation with the user:
"What specific topic or angle? Here are suggestions based on your messaging pillars:
From Pillar 1 ({pillar 1}):
- {topic idea A}
- {topic idea B}
From Pillar 2 ({pillar 2}):
- {topic idea C}
- {topic idea D}
From Pillar 3 ({pillar 3}):
- {topic idea E}
Or tell me your own topic."
Session save after answer:
echo '{"phase": "topic_choice", "topic": "TOPIC"}' >> "$_SESSION_FILE"
Create the content tailored to ALL of these simultaneously:
Present the full draft to the user inline first, then ask:
"Here's the draft. Read it through, then tell me:
A) Ship it -- write to file as-is B) Adjust the tone (more casual / more professional / more technical) C) Change the hook -- it's not grabbing me D) Shorter / longer E) Specific feedback -- tell me what to change"
Ask the user directly in conversation. Iterate until the user says "ship it."
After the text content is drafted and approved, decide what visual treatment this content needs.
Read the visual strategy framework:
_SKILL_DIR=$(dirname "$(find $HOME/.codex/skills -path "*/forbotsake-marketing-start/SKILL.md" -type f 2>/dev/null | head -1)")
Read $_SKILL_DIR/../knowledge/frameworks/visual-strategy.md for the decision matrix.
Decision logic:
none: hot takes, replies, technical deep-dives, short threads (<280 chars), quick tips, HN poststext-card: stat highlights ("2.3x faster"), quote cards, key takeaways, listicle items, comparison tables. Use when the content IS the visual.ai-image: launch announcements, storytelling posts, blog headers, Product Hunt assets. Use when the visual adds meaning beyond text (a metaphor, a scene, an emotion).video: product demos, launch teasers, explainer shorts. Use for high-impact launch content or when showing the product in action.Channel defaults:
ai-image for hero (tweet 1), none for other tweetstext-card if stat/quote, ai-image if storytelling, none if hot takeai-image or text-card (LinkedIn posts with images get 2x engagement)ai-image for featured/OG imageai-image for gallerynone or text-card (optional)none (these platforms value text over visuals)Generate the visual_prompt by combining:
prompt_prefix (style, colors, mood)Generate visual_alt accessibility text describing the image concept.
Interactive mode: Tell the user the visual treatment decision and why. If they disagree, let them override. Orchestrated mode: Auto-decide and proceed.
If visual_treatment is none, skip this phase.
If visual_treatment is text-card:
local-satori provider is available in media-providers.mdbun run $_SKILL_DIR/../bin/src/satori-card.ts --content {content_file} --brand brand.md --output content/{date}-{channel}-{slug}-visual-1.png --type {quote|stat|title|takeaway} --dimensions {channel-appropriate dimensions}curl -fsSL https://bun.sh/install | bash"If visual_treatment is ai-image:
https://gemini.google.com via Chrome automationvisual_prompt into compose areajavascript_tool: find the generated image element, draw it to a canvas, call canvas.toDataURL('image/png'), decode and save to content/ directoryImage review flow (interactive mode only):
After generating the image, show it and ask via direct conversation with the user:
"Here's the generated image for your {channel} post."
A) Use this image B) Regenerate with a different prompt (I'll refine it) C) Switch to text-card instead D) Skip visual for this post
Max 3 regeneration attempts. On 4th attempt, fall back to the best one or skip.
Orchestrated mode: Auto-accept the first generated image. If generation fails, set visual_status: failed and continue.
If visual_treatment is video:
content/{date}-{channel}-{slug}-video-1.mp4Create the content directory if it doesn't exist:
mkdir -p content
Write to content/{date}-{channel}-{topic-slug}.md with this schema:
---
schema_version: 2
generated_by: forbotsake
generated_at: {ISO timestamp}
channel: {x-thread|x-tweet|blog|linkedin|email|reddit|hackernews|producthunt}
status: draft
messaging_pillar: {which pillar this content supports}
topic: {topic description}
estimated_publish_date: {from calendar if available, else blank}
visual_treatment: {none|text-card|ai-image|video}
visual_prompt: "{the prompt used or to be used for generation}"
visual_placement: {hero|inline|thumbnail}
visual_count: {number of visuals, default 1}
visual_status: {generated|failed|pending|skipped}
visual_alt: "{accessibility description of the visual}"
visual_provider: "{provider name used, e.g., gemini-browser, local-satori}"
---
# {Content Title}
Channel: {channel}
Type: {content type}
Target: {ICP description from strategy.md}
---
{THE ACTUAL CONTENT}
---
## Publishing Notes
- **Best time to post:** {channel-specific recommendation}
- **Hashtags/tags:** {if applicable, 3-5 relevant ones}
- **CTA:** {the single call-to-action in this piece}
- **Thread to conversation:** {what topic to engage on if people reply}
- **Metrics to watch:** {what to measure for this specific piece}
- **Visual:** {visual_treatment} via {provider} — {path to visual file or "prompt-only"}
Confirm the file was written:
"Content written to
content/{filename}.Before publishing, run
/forbotsake-content-checkto verify it's on-brand and on-strategy. It catches things like messaging drift and weak CTAs."
After writing the file, read it back and verify:
If any check fails, tell the user what's weak before confirming the file write.
Orchestrated mode (ORCHESTRATED is 1): Skip this phase entirely. Do NOT suggest next skills or show the checklist. The orchestrator (forbotsake-go) handles what comes next. Simply confirm: "Content written to content/{filename}." Then stop.
Interactive mode (ORCHESTRATED is 0): Tell the user:
"Content is ready at
content/{filename}.Next step:
/forbotsake-content-checkto review it against your brand voice, messaging pillars, and channel format before publishing.Quick checklist before you post:
- [ ] Read it out loud -- does it sound like you?
- [ ] Check for typos (AI-generated content sometimes has subtle weirdness)
- [ ] Make sure any links work
- [ ] If there's an image/visual needed, create it
After publishing, track the metrics in the Publishing Notes section."
Remove the session file on successful completion:
rm -f "$_SESSION_FILE" 2>/dev/null
testing
Upgrade forbotsake to the latest version. Detects install type (git clone vs vendored), runs the upgrade, and shows what's new. Use when: "upgrade forbotsake", "update forbotsake", "get latest version", "forbotsake update".
research
Stage 3: RESEARCH (competitors). Analyzes 3-5 competitors to find messaging whitespace and positioning gaps. Produces competitor-analysis.md with a messaging matrix showing what each competitor says, what's missing, and where you can win. Use when: "competitor analysis", "competitive research", "what are others doing", "market landscape", "who am I competing with", "spy on competitors", "messaging whitespace", "differentiation research". Proactively invoke when the user mentions competitors or asks how to differentiate.
development
Stage 4.5: SHARPEN. Takes a specific outreach target (person or organization) and produces a deep execution plan with contextual research, relationship mapping, angle selection, and a multi-touch sequence. Reads your founder profile and strategy to leverage warm paths and unfair advantages. Use when: "refine this plan", "go deeper on this", "sharpen execution", "how do I approach [person]", "outreach to [person]", "target [person/org]", "approach [person]", "engage [org]". Proactively invoke when the user mentions approaching a specific person or organization as part of their marketing strategy. Requires: strategy.md (from /forbotsake-marketing-start).
data-ai
Stage 9: MEASURE. Reviews what you published, analyzes performance data, and produces a retro report with evidence-based recommendations. Tells you what to double down on, what to drop, and what to try next. Use when: "what worked", "marketing retro", "measure results", "review performance", "which content performed best", "should I keep doing this". Proactively invoke one week after /forbotsake-publish was last run.