Skip to content
Back to skills

Gen Social

ASecurity

Generate platform-specific social media copy and images for content distribution

  • 1,531 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
ai-agentspythongobashgit

Works with

  • cursor

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add hashgraph-online/awesome-codex-plugins --skill gen-social --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Gen Social?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Gen Social
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/hashgraph-online-gen-social/badge)](https://www.skillsdirectory.com/skills/hashgraph-online-gen-social)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: gen-social
version: "1.3.0"
description: Generate platform-specific social media copy and images for content distribution
---

## Objective

Transform the daily AI news report into platform-optimized social media content. Generates ready-to-post copy and adapted images for multiple platforms, with support for multiple accounts and styles per platform.

**Phase 1 scope**: Generate copy files + images. No auto-publish (future phase).

---

## Supported Platforms

### X / Twitter

| Constraint | Value |
|------------|-------|
| Single tweet | 280 characters max |
| Thread | Unlimited tweets (recommended 5-15) |
| Images per tweet | Up to 4, 16:9 or 1:1 |
| Hashtags | 2-3 per tweet |
| Mentions | @handle format |

### Xiaohongshu (Little Red Book)

| Constraint | Value |
|------------|-------|
| Title | 20 characters max |
| Body | 1000 characters max |
| Images | Up to 9 per post, 3:4 or 1:1 (carousel) |
| Tags | 5-10 # tags at end of body |
| Emoji | Heavy use expected (platform culture) |

---

## Style Definitions

### Custom Personas & Styles

Templates define the voice, tone, and format for each channel. This repo ships with **one example template per platform** as a starting point:

- **X**: `templates/x/insider.md` (Tech Insider persona)
- **Xiaohongshu**: `templates/xiaohongshu/educational.md`

To create your own persona or style:

```bash
# X persona
cp skills/gen-social/templates/x/insider.md skills/gen-social/templates/x/my-persona.md

# Xiaohongshu style
cp skills/gen-social/templates/xiaohongshu/educational.md skills/gen-social/templates/xiaohongshu/my-style.md
```

Edit the copy, then set your channel config `style` field to match the filename (e.g. `"style": "my-persona"`). Custom templates in these directories are gitignored — your personas stay local.

### X Styles

Each X style has a distinct **persona** — a consistent voice and perspective that makes the account feel like a real person, not a news feed.

**Available styles:**

| Style | Persona | Template | Tone | Default Items | Min Score |
|-------|---------|----------|------|---------------|-----------|
| `briefing` | Daily Briefing | `templates/x/briefing.md` | Clean scannable index + infographic image, neutral | 5 | 6 |
| `insider` | Razor | `templates/x/insider.md` | Devastating one-liners, deadpan, says the quiet part loud | 3 | 7 |
| `commentary` | Builder | `templates/x/commentary.md` | First-person practical, opinionated hot takes, @levelsio meets @karpathy | 3 | 8 |
| `thread` | Hype-Free Analyst | `templates/x/thread.md` | Data-driven thread, contrarian, pattern recognition, @Benedict Evans | 5 | 6 |

### Xiaohongshu Styles

**Available styles:**

| Style | Template | Tone | Default Items | Min Score |
|-------|----------|------|---------------|-----------|
| `news-briefing` | `templates/xiaohongshu/news-briefing.md` | Telegraph speed-scan, priority indicators | 5-8 | 5 |
| `educational` | `templates/xiaohongshu/educational.md` | Structured explainer, numbered, "one article to understand it all" | 3-5 | 6 |
| `recommendation` | `templates/xiaohongshu/recommendation.md` | Enthusiastic discovery, heavy emoji, "must see!" | 3 | 7 |

---

## Channel Configuration

A **channel** is the core abstraction: one channel = one platform + one style + one language + content selection rules. Each channel produces an independent set of output files.

### Channel Config File

Located at `~/.config/morning-ai/social_channels.json` (or path set via `SOCIAL_CHANNELS_FILE`).

```json
[
  {
    "id": "x_insider_en",
    "platform": "x",
    "style": "insider",
    "lang": "en",
    "items": 3,
    "min_score": 7,
    "image": true,
    "image_aspect": "16:9"
  },
  {
    "id": "x_thread_en",
    "platform": "x",
    "style": "thread",
    "lang": "en",
    "items": 5,
    "min_score": 6,
    "image": true,
    "image_aspect": "16:9"
  },
  {
    "id": "xhs_kepu_zh",
    "platform": "xiaohongshu",
    "style": "educational",
    "lang": "zh",
    "items": 5,
    "min_score": 6,
    "image": true,
    "image_aspect": "3:4",
    "image_count": 4
  },
  {
    "id": "xhs_zhongcao_zh",
    "platform": "xiaohongshu",
    "style": "recommendation",
    "lang": "zh",
    "items": 3,
    "min_score": 7,
    "image": true,
    "image_aspect": "3:4",
    "image_count": 3
  }
]
```

### Channel Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | Unique channel identifier, used in output filenames |
| `platform` | string | Yes | `x` or `xiaohongshu` |
| `style` | string | Yes | Style name (see Style Definitions above) |
| `lang` | string | Yes | Language code: `en`, `zh`, `ja`, etc. |
| `items` | number | No | Max items to include (default: from template) |
| `min_score` | number | No | Minimum score threshold (default: from template) |
| `image` | boolean | No | Generate images for this channel (default: `false`) |
| `image_aspect` | string | No | Image aspect ratio: `16:9`, `1:1`, `3:4` (default: platform default) |
| `image_count` | number | No | Number of carousel images for xiaohongshu (default: 1) |
| `image_style` | string | No | Override image style: `classic`, `dark`, `glassmorphism`, `newspaper`, `tech`, `cover-hook` (default: persona's recommended style). Note: `cover-hook` is special — it is intended only for the FIRST image of a Xiaohongshu carousel (the thumbnail in the discovery feed). Subsequent images should use the channel's regular style. |
| `include_types` | array | No | Filter by content types, e.g. `["model", "product"]` (default: all) |
| `kol_voice_only` | boolean | No | If `true`, only items with `is_kol_voice: true` are eligible for selection. Used by the dedicated KOL channel so that KOL original commentary becomes the post's main topic, not embedded inside an unrelated post. (default: `false`) |
| `conditional` | string | No | Conditional run gate. Currently supported: `viral_only` (max score ≥ `min_score`) and `kol_voice_available` (at least one item with `is_kol_voice: true` and `score ≥ min_score`). If the gate fails, the channel produces an empty manifest entry (no copy file, no images) and downstream consumers (e.g. foreman publish adapter) should skip the run gracefully. |

### Quick Setup (Single Channel via Env Vars)

When only one channel is needed, use env vars instead of a JSON file:

```bash
SOCIAL_PLATFORM=x
SOCIAL_STYLE=insider
SOCIAL_LANG=en
```

This creates a single channel with id `{platform}_{style}_{lang}`.

---

## Content Selection Rules

For each channel, select items from the daily report data:

1. **Filter by score**: only items with `importance >= min_score`
2. **Filter by type**: if `include_types` is set, only those content types
3. **Filter by KOL voice flag**: if `kol_voice_only: true` is set, only items where `is_kol_voice: true`
4. **Sort by score**: highest first
5. **Limit**: take top N items (from channel `items` setting or template default)
6. **Translate**: if source data language differs from channel `lang`, translate content. Entity names (proper nouns) stay unchanged.

### Conditional Channels (Pre-Selection Gate)

**This check runs BEFORE the 5 steps above.** Some channels only make sense on certain days (e.g. a viral-only channel that should only post when there's a genuinely big story). These channels declare a `conditional` field — the value tells the pipeline what gate to apply:

| `conditional` value | Gate logic |
|---|---|
| (omitted) | Always run (default) |
| `viral_only` | Channel runs ONLY if `max(item.importance for item in today's data) >= channel.min_score`. If no item clears the threshold, the channel is skipped. |
| `kol_voice_available` | Channel runs ONLY if at least one item has `is_kol_voice: true` AND `score >= channel.min_score`. If no qualifying KOL voice exists today, the channel is skipped. Used by the dedicated KOL channel so that on quiet KOL days the post is not forced. |

**When a conditional channel is gated off:**

1. Do NOT generate a copy file or images for this channel
2. Still emit a manifest entry, but with a `skipped: true` field and a `skip_reason` explaining why (e.g. `"max score 7.8 < required 8.5"`)
3. Required manifest entry shape for skipped conditional channels:
   ```json
   {
     "id": "xhs_zhongcao_viral_zh",
     "platform": "xiaohongshu",
     "style": "single-topic",
     "skipped": true,
     "skip_reason": "max score 7.8 < required 8.5"
   }
   ```
4. Downstream publish adapters (e.g. foreman `daily-publish-xhs-2-viral`) detect `skipped: true` and mark their run as SKIPPED rather than FAILED.

**Why this matters**: it lets us schedule a 12:00 viral-only post on the cron without generating empty/low-quality posts on slow news days. The viral channel only fires content when there's something worth firing about.

---

## Copy Generation

For each channel:

1. Read the template: `{SKILL_DIR}/skills/gen-social/templates/{platform}/{style}.md`
2. Select items using Content Selection Rules above
3. Generate copy following the template's format rules, character limits, and tone
4. Validate character counts against platform limits

### Post-Generation Checklist

- [ ] Character count within platform limits (280/tweet for X, 20 title + 1000 body for Xiaohongshu)
- [ ] Language matches channel `lang` setting
- [ ] Entity names preserved as proper nouns (not translated)
- [ ] Source links included where template requires them
- [ ] Hashtags/tags present and relevant
- [ ] No score numbers displayed in copy (scores are internal only)

---

## Image Generation

Social images reuse the existing image generation infrastructure (`lib/image_gen.py`) and the same providers (Gemini/GPT/MiniMax).

### Image Language Rules

Image text language **must match the target platform**, not the report source language:

| Platform | Image Language | Header Text |
|----------|---------------|-------------|
| **X** | **English only** | "MorningAI" |
| **Xiaohongshu** | **Chinese only** | "MorningAI" |

All card titles, bullet points, section headers, and any text rendered on the image must be in the platform's language. Entity names (proper nouns like OpenAI, DeepSeek) remain unchanged.

### Persona–Image Style Mapping

Each persona/style has a **recommended image style** that matches its voice. This overrides the global `IMAGE_STYLE` setting for social images.

#### X Styles

| Persona | Recommended Image Style | Images | Why |
|---------|------------------------|--------|-----|
| **briefing** (Daily Briefing) | `classic` | 1 cover + per-category (model/product/benchmark/funding) | Clean editorial, professional — cover indexes the day, per-category images go deeper |
| **insider** (Tech Insider) | `tech` | 0 | Razor is pure text — images dilute the punchline |
| **commentary** (Builder) | `dark` | 0 | Builder takes stand alone — bold text, no visual crutch |
| **thread** (Hype-Free Analyst) | `classic` | 1 cover | Clean editorial magazine — serious, data-focused, no visual noise |

#### Xiaohongshu Styles

| Style | Default Image Style | Images | Why |
|-------|---------------------|--------|-----|
| **recommendation** | `glassmorphism` | 3 carousel | Frosted glass, warm tones — lifestyle/discovery aesthetic |
| **educational** | `classic` | 4 carousel | Clean editorial — structured, authoritative content |
| **news-briefing** | `newspaper` | 1 | Classic newsprint — information-dense, professional |

> **IMPORTANT**: If the channel config has an explicit `image_style` field, that takes absolute priority over the defaults above. Do NOT include style labels or content-type annotations in image prompts.

#### Briefing Per-Category Images

The **briefing** channel generates additional per-category images beyond the cover, mirroring how `gen-infographic` handles per-type infographics:

| Image | Filename | Generated When |
|-------|----------|----------------|
| Cover | `social_{DATE}_x_briefing_en_cover.png` | Always |
| Model | `social_{DATE}_x_briefing_en_model.png` | ≥1 Model item with score ≥ min_score |
| Product | `social_{DATE}_x_briefing_en_product.png` | ≥1 Product item with score ≥ min_score |
| Benchmark | `social_{DATE}_x_briefing_en_benchmark.png` | ≥1 Benchmark item with score ≥ min_score |
| Funding | `social_{DATE}_x_briefing_en_funding.png` | ≥1 Funding item with score ≥ min_score |

Per-category images:
- Same classic style as cover, 16:9 landscape
- Header: "MorningAI — {Type} Updates" (no date — consistent with infographic per-type images)
- Include ALL qualifying items of that type (not limited to the channel `items` cap)
- Each card: entity + event + 2-3 bullet points

The recommended style is a default — channels can override via `image_style` field in the channel config. If not set, the persona's recommended style is used. If no persona mapping exists, falls back to the global `IMAGE_STYLE` setting.

### Platform-Specific Image Adaptation

**X images**:
- Aspect ratio: 16:9 (landscape) or 1:1 (square)
- **All text in English** — titles, bullets, headers, everything
- Header: "MorningAI"
- Apply the persona's recommended image style (see mapping above)
- Single image per tweet (or up to 4 for threads)

**Xiaohongshu images**:
- Aspect ratio: 3:4 (portrait) or 1:1 (square)
- **All text in Chinese** — titles, bullets, headers, everything
- Header: "MorningAI"
- Apply the style's recommended image style (see mapping above), plus Xiaohongshu-specific adaptations:
  - Larger font sizes for mobile readability
  - Rounded card corners (16px)
  - Emoji-style bullet markers
- Carousel strategy (when `image_count > 1`):
  - Image 1: Cover overview — top headlines, eye-catching title
  - Image 2-N: Detail pages — 1-2 items per image with expanded bullet points
  - Last image: Follow/subscribe CTA (optional)

### Image Prompt Template

```
{ASPECT} infographic, {HEADER_TEXT} {YYYY-MM-DD}, ALL text content in {LANG}.

Total news items: {N}

News cards (display EXACTLY {N} cards):

Card 1: {Entity name} {Event subject} {Core event verb phrase}
- {Point 1}
- {Point 2}
- {Point 3}

(... list according to actual item count ...)

CRITICAL RULES:
- ALL text on this image MUST be in {LANG} — titles, bullet points, headers, labels, everything
- Entity names are proper nouns (OpenAI, DeepSeek, Cursor) — keep as-is, do NOT translate
- Do NOT display ANY platform names on the image (no "小红书", "Xiaohongshu", "特刊", "Twitter", "X" etc.) — the image is content, not an ad for the platform
- Header text: "{HEADER_TEXT}"
- Each card title MUST include: Entity name + Event subject + Event description
- Display complete titles, do NOT truncate
- Do NOT display score numbers, score badges, or importance markers
- Do NOT invent items not listed
- Display ALL bullet points for each card
- Maximize content area — card titles and bullet points are the primary focus

ANTI-AI-SLOP RULES (mandatory — ref: huashu-design):
- NO purple/lavender/violet gradients — this is the #1 "AI-generated" tell
- NO emoji as bullet markers or icons — use typographic bullets (·, —, or numbered)
- NO rounded-corner cards with colored left border accent — the 2020-2024 AI slop archetype
- NO Inter/Roboto/Arial/system fonts as display — use serif display fonts (Noto Serif, Source Serif, Georgia)
- NO neon glow, glassmorphism, or frosted glass effects
- NO decorative icons per heading — if an icon doesn't carry information, remove it
- NO flat digital color blocks — use paper/material texture feel
- Color restraint: black + cream/white + ONE accent color max. Do NOT invent extra colors
- Typography IS the visual language — let font weight, size, and spacing create hierarchy, not color/shape decoration
- One detail at 120%, everything else at 80% — not uniformly polished, but sharp where it counts
- Describe mood, not pixel coordinates — "feels like a morning broadsheet" beats "title at top, 36pt, centered"

{STYLE_BLOCK}
{PLATFORM_STYLE_ADDON}
```

**Variable substitution:**
- `{HEADER_TEXT}` → "MorningAI" for X, "MorningAI" for Xiaohongshu
- `{LANG}` → "English" for X, "Chinese" for Xiaohongshu
- `{STYLE_BLOCK}` → from the persona's recommended image style (see `skills/gen-infographic/SKILL.md` Style Presets)

**`{PLATFORM_STYLE_ADDON}`** for Xiaohongshu — **use the matching style variant based on the channel's `image_style` field**:

**When `image_style` = `glassmorphism`:**
```
Additional Xiaohongshu adaptation (glassmorphism):
- ALL text must be in Chinese (except entity proper nouns)
- Soft gradient background (lavender to pale rose)
- Semi-transparent frosted white cards with backdrop blur
- Use rounded corners (16px) on all cards
- Larger title font (22pt bold) for mobile readability
- Warm accent colors: coral (#FF6B6B), soft pink (#FFB4B4), lavender (#E8EAF6)
- Emoji bullet markers (colored dots or sparkle symbols)
- Clean, fresh, lifestyle-magazine aesthetic
- Generous padding and line spacing for mobile screens
```

**When `image_style` = `newspaper`:**
```
Additional Xiaohongshu adaptation (newspaper):
- ALL text must be in Chinese (except entity proper nouns)
- Mood: feels like unfolding a quality morning broadsheet — authoritative, calm, information-dense, zero decoration
- Warm cream background (#FFF8E7) with subtle paper grain texture — NOT flat digital white
- Typography IS the design: bold serif headers (Noto Serif CJK / Source Han Serif / Georgia feel), clean sans-serif body
- Typographic hierarchy through font weight (700 headers, 400 body) and size contrast — NOT through color or shape
- Thin black hairline rules (1px) between sections, like column dividers in a broadsheet
- NO card backgrounds, NO rounded corners, NO shadows, NO colored borders — pure typographic layout
- Color: cream + deep black + crimson (#DC143C) accent ONLY — crimson used sparingly (dateline, one key number, or a thin rule)
- Bullet markers: typographic only (· or —), NEVER emoji
- One signature detail at 120%: the headline typography should feel crafted, like a newspaper masthead
- Generous line spacing (1.6+) for mobile readability, but information-dense — fill the space with content, not decoration
- DO NOT use gradients, frosted glass, glow effects, or any "digital" aesthetic
- The image should look like it was typeset, not generated
```

**When `image_style` = `cover-hook`:** (FIRST image of XHS carousel ONLY — this is the discovery-feed thumbnail)
```
Additional Xiaohongshu adaptation (cover-hook):
- ALL text must be in Chinese (except entity proper nouns)
- Mood: a newspaper EXTRA edition headline — urgent, bold, one statement that demands attention, but with editorial class, not digital loudness
- 3:4 single card, super-large title text occupying ≥ 50% of canvas
- Title text = the post title or its strongest 1-line variant (e.g., "Cursor 估值飙到 500 亿")
- ONE single subtitle line below with the most striking concrete data point (e.g., "3 年达 20 亿 ARR · 史上最快 B2B")
- Newspaper headline poster aesthetic:
  - Warm cream (#FFF8E7) with bold black serif title, OR deep charcoal (#1A1A1A) with cream/off-white serif title
  - Bold serif typography (Noto Serif CJK / Source Han Serif / Georgia feel), weight ≥ 700
  - Paper grain texture — NOT flat digital color blocks
- Thin crimson (#DC143C) hairline rule above or below the headline — the ONE signature detail at 120%
- Optional: small "MorningAI" masthead at very top in small caps, like a newspaper nameplate — restrained, not decorative
- NO bullet points, NO emoji, NO icons, NO small annotations — single statement only
- NO body text, NO list of items, NO date stamps — only the headline + one subtitle line
- NO gradients, NO glow, NO frosted glass, NO rounded corners — this is typeset, not generated
- The cover should look like a broadsheet front page that was photographed, not a Canva template
```

**When `image_style` = `classic`:**
```
Additional Xiaohongshu adaptation (classic):
- ALL text must be in Chinese (except entity proper nouns)
- Off-white background (#F5F5F0), clean editorial magazine layout
- Card-based layout with subtle shadows
- Navy (#1B2A4A), coral (#E8634A), teal (#2A9D8F) accent palette
- Use rounded corners (8px) on cards
- Larger title font for mobile readability
- Structured, authoritative visual hierarchy
- Generous padding and line spacing for mobile screens
```

**`{PLATFORM_STYLE_ADDON}`** for X (append to any base style):
```
Additional X/Twitter adaptation:
- ALL text must be in English (except entity proper nouns)
- Optimized for timeline scroll — key info visible at small preview size
- High contrast text for readability on mobile
```

### Generating Images

Use the same methods as Step 4 (gen-infographic):

**Option A** — Native tool (if supported):
Generate each image using built-in image generation capability.

**Option B** — Python script batch mode:
Build a manifest JSON and run:
```bash
cd {SKILL_DIR} && python3 skills/gen-infographic/scripts/gen_infographic.py --batch {CWD}/social/manifest_images.json
```

Manifest entries support `"aspect_ratio": "3:4"` for Xiaohongshu images.

#### Cover-Hook for Xiaohongshu Carousels

For Xiaohongshu channels with `image_count >= 2` AND style `recommendation` or `single-topic`, the **first image** of the carousel is the discovery-feed thumbnail and MUST use the `cover-hook` style addon (regardless of the channel's regular `image_style`):

- Image 1 → use `{PLATFORM_STYLE_ADDON}` for `cover-hook`. Content = post title + ONE strongest data point. No bullet points, no list of items.
- Image 2..N → use the channel's regular `image_style` (e.g., `newspaper`). Content = the per-item news cards as before.

This applies because the first image is what stops the scroll in the discovery feed; an information-dense newspaper card is unreadable as a thumbnail. Channels that already declare `image_style: cover-hook` explicitly should apply it to ALL images (rare — typically reserved for single-image posts).

---

## Output Files

All output goes to `{CWD}/social/` directory.

### Naming Convention

```
social/
├── social_{DATE}_{channel_id}.md              # Copy file
├── social_{DATE}_{channel_id}_cover.png       # Single/cover image
├── social_{DATE}_{channel_id}_{N}.png         # Carousel image N
└── social_{DATE}_manifest.json                # Output index
```

### Manifest Format

> **MANDATORY SCHEMA — DO NOT IMPROVISE FIELD NAMES OR PATHS**
>
> The manifest is consumed by downstream tools that match on exact field
> names and assume flat filenames. Past LLM-generated output has drifted
> in ways that silently break consumers (`channel_id` instead of `id`,
> `copy` instead of `copy_file`, paths prefixed with `social/`). This
> section is the contract — produce it byte-for-byte as specified.

**Required top-level shape:**
- `channels` MUST be a JSON **array** (list). Never a dict keyed by id.

**Required per-channel fields** — use these exact names, no aliases:

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `id` | string | Yes | Channel identifier. **Field name is `id`, NOT `channel_id`.** |
| `platform` | string | Yes | `x` or `xiaohongshu` |
| `style` | string | Yes | Style name |
| `lang` | string | Yes | `en`, `zh`, `ja`, etc. |
| `copy_file` | string | Conditional | **Filename only** (no directory prefix). Field name is `copy_file`, NOT `copy`. **Omit this field if `skipped: true`.** |
| `images` | array<string> | Conditional | **Filenames only** (no directory prefix). Empty list `[]` if no images. **Omit this field if `skipped: true`.** |
| `items_used` | number | No | Count of source items included |
| `skipped` | boolean | No | Set to `true` for conditional channels that did not meet their gate (e.g. viral_only with no item ≥ min_score). When `true`, omit `copy_file` and `images`. |
| `skip_reason` | string | Conditional | Required when `skipped: true`. Short explanation, e.g. `"max score 7.8 < required 8.5"`. |

**Path rules — applies to `copy_file` and every entry in `images`:**
- Write the **bare filename** as it appears under `social/` (e.g. `social_2026-04-18_xhs_zixun_zh.md`).
- Do **NOT** include any directory component — no `social/`, no `./`, no absolute path.
- Wrong: `"copy_file": "social/social_2026-04-18_xhs_zixun_zh.md"` ❌
- Right: `"copy_file": "social_2026-04-18_xhs_zixun_zh.md"` ✅

**Canonical example** (copy this shape exactly):

```json
{
  "date": "2026-04-14",
  "channels": [
    {
      "id": "x_insider_en",
      "platform": "x",
      "style": "insider",
      "lang": "en",
      "copy_file": "social_2026-04-14_x_insider_en.md",
      "images": ["social_2026-04-14_x_insider_en_cover.png"],
      "items_used": 3
    },
    {
      "id": "xhs_kepu_zh",
      "platform": "xiaohongshu",
      "style": "educational",
      "lang": "zh",
      "copy_file": "social_2026-04-14_xhs_kepu_zh.md",
      "images": [
        "social_2026-04-14_xhs_kepu_zh_1.png",
        "social_2026-04-14_xhs_kepu_zh_2.png",
        "social_2026-04-14_xhs_kepu_zh_3.png",
        "social_2026-04-14_xhs_kepu_zh_4.png"
      ],
      "items_used": 5
    },
    {
      "id": "xhs_zhongcao_viral_zh",
      "platform": "xiaohongshu",
      "style": "single-topic",
      "lang": "zh",
      "skipped": true,
      "skip_reason": "max score 7.8 < required 8.5"
    }
  ]
}
```

**Forbidden variants** (all of these have been observed in real output and
break downstream consumers — do not produce them):

```json
// ❌ WRONG — "channel_id" is not a recognized field
{ "channel_id": "xhs_zixun_zh", ... }

// ❌ WRONG — "copy" is not a recognized field
{ "id": "xhs_zixun_zh", "copy": "...", ... }

// ❌ WRONG — paths must be bare filenames
{ "copy_file": "social/social_2026-04-18_xhs_zixun_zh.md",
  "images": ["social/social_2026-04-18_xhs_zixun_zh_1.png"] }

// ❌ WRONG — channels must be a list, not a dict keyed by id
{ "channels": { "xhs_zixun_zh": { ... } } }
```

Before writing the manifest file, mentally diff your output against the
canonical example above. If any field name, structure, or path shape
differs, fix it before writing.

---

## Workflow Summary

1. Check if social content is enabled (`SOCIAL_ENABLED=true`)
2. Load channel configuration (JSON file or env vars)
3. For each channel:
   a. **Conditional gate**: if the channel has a `conditional` field, evaluate the gate. If it fails, write a manifest entry with `skipped: true` + `skip_reason` and continue to the next channel — do NOT generate copy or images.
   b. Read the channel's template file
   c. Select top items from report data (score filter + item limit)
   d. Generate copy following template rules and character limits
   e. Write copy to `social/{DATE}_{channel_id}.md`
   f. If `image: true` — build image prompts and generate platform-adapted images. For XHS carousels with style `recommendation` or `single-topic`, the FIRST image uses the `cover-hook` style addon; subsequent images use the channel's regular `image_style`.
   g. Write images to `social/{DATE}_{channel_id}_{N}.png`
4. Write manifest to `social/{DATE}_manifest.json`

---

## Notes

- Channels are fully independent — each reads from the same daily data but produces separate output
- Multiple channels can target the same platform with different styles, languages, or content focus
- **Image language follows the platform**: X images are always English, Xiaohongshu images are always Chinese — regardless of the channel `lang` setting for copy
- **Image style follows the persona**: each persona/style has a recommended image style that matches its voice (see Persona–Image Style Mapping). Channels can override via `image_style` field
- All content must respect platform character limits — validation is mandatory
- Entity names are proper nouns and must NOT be translated regardless of `lang` setting
- When the same item appears in multiple channels, each channel generates its own adapted version independently

Files in this skill

  • SKILL.md27.4 KB
  • agents/openai.yaml233 B
  • templates/x/briefing.md5.9 KB
  • templates/xiaohongshu/news-briefing.md3.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.