WeChat Autopublish
Overview
This skill turns source material into a polished WeChat Official Account article for a personal subscription account.
The core value is not generic writing advice. The skill is designed as a professional publishing pipeline for the owner's personal brand, with a strong bias toward:
- Feishu doc → WeChat article conversion
- topic → ready-to-publish WeChat article
- practical / technical content rather than empty thought-leadership copy
It should:
Accept one of these inputs:
- a topic
- a Feishu/Lark document
- local Markdown/notes
- reference links
- an existing draft
Produce one of these final outputs:
- WeChat draft box article (preferred)
- ready-to-paste publishing package (fallback)
- draft review package (when explicitly requested)
Default to a professional technical blog workflow instead of generic content generation.
Support explicit copy-transfer mode when the source article is already strong enough to be published as a mostly intact WeChat copy, especially when the user asks to preserve both the detailed content and the original images.
The skill should keep the workflow stable, reusable, and focused on publishing, not brainstorming.
When to Use
Use this skill when:
- the user wants to publish or convert a Feishu doc into a WeChat article
- the user gives a topic and wants a publish-ready WeChat article
- the user asks to push content into the WeChat draft box
- the user says things like "公众号", "推文", "公号稿", "草稿箱", "发布文章"
- the user wants a repeatable publishing workflow rather than one-off writing help
- the user provides an internal document, design note, or technical summary and wants it transformed into external-facing WeChat content
- the user wants the agent to handle the full publishing job rather than just write a draft
The three most important trigger patterns are:
- Feishu doc publishing mode: publish this Feishu doc as a WeChat article
- Topic content-building mode: take this topic and build a publish-ready WeChat article for me
- Copy-transfer mode: the source article is already detailed and should be published as a near-complete copy with images retained, not condensed into a fresh rewrite
Do not use this skill for:
- general article writing without WeChat publishing intent
- enterprise service-account publishing workflows
- short social posts unrelated to the WeChat Official Account
- lightweight brainstorming that clearly does not need a publishing pipeline
Default Profile
This is a personal-brand publishing skill, not a generic content generator.
Personal voice baseline
The default writing style should follow the owner's blog voice at https://tyrantlucifer.com/ .
Based on representative posts such as 笔记工作流的最佳实践 and Fluss - 面向分析的实时流存储初探, the blog's recurring style has these traits:
- writes in first person, but keeps the focus on the topic rather than self-dramatization
- opens by stating the real background, problem, or motivation in a few sentences
- prefers a clean problem-oriented or evaluation-oriented structure instead of fluffy narrative
- often uses a flow like: motivation/context → concept/objective → architecture or selection criteria → concrete examples or setup notes → verification/takeaways
- uses comparison, step reasoning, and decision explanation when introducing a new tool or workflow
- keeps paragraphs short and readable, usually 2-4 sentences
- frequently uses section titles that directly describe the current stage or question
- uses concrete nouns, real components, and real scenarios instead of abstract inspirational language
- preserves important English names exactly when discussing open-source projects, systems, or technical terms
- uses first-person observation to add practical judgment, not to perform emotion
- prefers clear lists, structured evaluation points, architecture breakdowns, and workflow summaries
- values clarity, practicality, reproducibility, and reader utility over rhetorical flourish
- tends to explain choices and trade-offs rather than just stack buzzwords
Style fingerprint: Write like the blog owner: a senior big-data / infrastructure engineer who explains things in a calm, practical, structured, example-aware, and opinionated way. The article should feel like a real practitioner's technical blog post, not AI promotional copy.
Source posts sampled for this profile:
笔记工作流的最佳实践Fluss - 面向分析的实时流存储初探SeaTunnel连接器V1到V2的架构演进与探究
User-operating preferences for this skill (hard-won corrections from live use):
- One canonical copy of publishing scripts lives inside this skill, under
scripts/wechat_publisher/. Do not propose wrapper shells that merely call an external checkout. - When the user asks for “搬运” / copy / migration mode, preserve the source document's structure and detail first. Do not aggressively rewrite into a short editorial unless the user explicitly asks for that mode.
- When migrating Feishu docs to WeChat, treat callout blocks and whiteboard figures as first-class content, not decorative noise. They must be converted or migrated explicitly.
- When figures exist, migrate unique images intentionally. Avoid naive duplication that inflates figure count and produces messy layout.
- Publishing output should be actionable and concrete: title, digest, author, cover, migrated images, HTML quality, fallback path, and next action. Avoid long theoretical explanations when the user wants execution.
- Quality gate comes after migration completeness, not before. First make the copy faithful; then polish for publishing readability.
- The user expects active review of the HTML conversion path whenever rich Feishu content is involved, especially callouts, figures, tables, and appendix image placement.
Publishing defaults
Unless the user overrides:
- account:
main - preferred publishing mode: draft box
- article scale: roughly 2500-5000 words for long-form
- output language: Chinese
- structure: problem-oriented or workflow-oriented
- style: professional technical blog, not marketing copy
- translation rule: if source material is in English, publish in Chinese with original terms preserved where useful
Execution backend
This skill manages its own publishing scripts internally:
scripts/wechat_publisher/
This is the correct integration shape for this user. Do not vendor the entire upstream project tree, do not create a wrapper shell that merely calls an external checkout, and do not leave publishing scripts split across two unrelated locations.
The required integration pattern is:
- extract only the necessary publishing scripts into
scripts/wechat_publisher/ - keep required publishing assets together with those scripts when the scripts depend on them
- keep
wechat-publisher.yamlwith the extracted scripts, not at unrelated outer paths - expose thin shell entrypoints under
scripts/wp-*.shthat call the internal scripts directly
If the underlying publishing code expects its config at parent.parent / wechat-publisher.yaml, adjust the internal copy so the config lookup matches the skill's actual layout instead of assuming the old upstream directory shape.
Known converter pitfalls
lark-cli table output missing pipe delimiters
When fetching Feishu docs via lark-cli docs +fetch, exported Markdown tables may be missing leading/trailing |. Normalize them before gzh-design rendering so table structure is unambiguous and source fidelity can be checked.
Fix: After fetching, normalize table rows by adding | at start and end:
lines = content.split('\n')
new_lines = []
for line in lines:
if '|' in line and not line.strip().startswith('```'):
if line.count('|') >= 2:
if not line.strip().startswith('|'):
line = '|' + line
if not line.strip().endswith('|'):
line = line + '|'
new_lines.append(line)
### Known converter pitfalls
#### Use `gzh-design` as the canonical Markdown → HTML renderer
`gzh-design` is the **default and mandatory renderer** for new publishing runs. The internal `html_converter.py` is retained only as a legacy emergency fallback; do not choose it merely because it is deterministic or already wired into `publish.py`.
Canonical path:
1. normalize Feishu/Word/PDF/notes into clean WeChat-ready Markdown;
2. load `gzh-design` and read its `theme-index.md`, the selected theme library, and `common-components.md`;
3. render a pure `<section>…</section>` body fragment with inline styles and `<span leaf="">` text wrappers;
4. run `gzh-design/scripts/validate_gzh_html.py` until **ERROR=0 and WARNING=0**;
5. run `scripts/wp-html.sh <rendered.html>` for the copy-ready preview;
6. publish the rendered HTML with `scripts/wp-publish.sh <rendered.html> --title ...`, which uploads local/external HTML images to WeChat CDN before draft creation.
Theme behavior:
- if the user specifies a gzh-design theme, use it directly;
- otherwise recommend from the article type; in full-auto publishing, choose automatically and report the reason;
- do not mix components from different gzh-design themes;
- preserve the normalized Markdown as the content source of truth and the gzh-design HTML as the publishing artifact.
Legacy fallback is allowed only when `gzh-design` is unavailable or its component assets are damaged. The fallback must be explicit in the final report and uses `wp-html-legacy-md.sh` / `wp-publish-legacy-md.sh`.
**Mandatory HTML quality gate before publishing:**
1. gzh-design validator reports 0 ERROR and 0 WARNING;
2. no literal `**` remains in HTML;
3. no `src=""` appears;
4. no raw `<grid>`, `<callout>`, `<whiteboard>`, Mermaid code, or `flowchart`/`mindmap` remains;
5. expected links retain `href="https://..."`;
6. every expected figure has a non-empty local/CDN image source;
7. after draft creation, `draft/get` confirms content and figures survived WeChat sanitization.
### Table rows MUST start and end with `|`
`html_converter.py` detects table rows by checking `stripped.startswith("|")`. Markdown tables like:
| 维度 | 传统数据湖 | 多模态数据湖 |
|---|---|---|
| 数据类型 | 结构化/半结构化 | 任意模态 |
will NOT be recognized as tables. They must be:
| 维度 | 传统数据湖 | 多模态数据湖 |
|---|---|---|
| 数据类型 | 结构化/半结构化 | 任意模态 |
Always normalize table rows to start/end with `|` before passing to the HTML converter.
### Table cells: `flush_table()` must call `process_inline()` on cell contents
The original `flush_table()` in `html_converter.py` inserted raw cell text into `<th>`/`<td>` tags without processing inline Markdown (`**bold**`, links, etc.). Fix: call `process_inline(c)` on each cell value.
### Table headers: strip `**bold**` markers to avoid yellow highlight
`process_inline()` converts `**text**` to `<strong>` with yellow highlight background (`#fff18a`). In table headers (`<th>` with dark blue background), this creates unreadable text. Fix: strip `**` markers from header cells before calling `process_inline()`:
```python
def clean_th_text(t):
t = re.sub(r'\*\*([^*]+)\*\*', r'\1', t)
return process_inline(t)
Table rows must start AND end with | (critical)
html_converter.py detects table rows via stripped.startswith("|"). Rows without leading | are treated as plain paragraphs and break the entire table.
See "Markdown tables MUST start and end with |" section above for the fix script, or references/table-format-fix.md for a standalone fixer script.
Table cell inline markdown needs explicit process_inline() call
Fixed in 2026-07-01: flush_table() now calls process_inline() on both <th> and <td> cell contents. Previously, **bold** in table cells rendered as literal asterisks.
Table header **bold** causes yellow highlight on dark background
Fixed in 2026-07-01: Table headers now strip **bold** markers before processing, since the strong style's yellow #fff18a background clashes with the dark blue #0b1530 header background.
Feishu <grid> / <column> blocks need manual preprocessing
The strong style in html_converter.py includes background: #fff18a (yellow highlight). When table header cells contain **bold** markers, the yellow highlight appears on the dark blue #0b1530 th background, making text unreadable.
Fix: In flush_table(), strip **bold** markers from table header (th) cells before passing to process_inline(), since <th> already has font-weight: 600:
def clean_th_text(t):
t = re.sub(r'\*\*([^*]+)\*\*', r'\1', t)
return process_inline(t)
Apply to the i == 0 (header row) branch only. Body <td> cells should still use process_inline() to preserve inline markdown formatting.
Table cell markdown not rendered
The original flush_table() passed cell content directly into <td> without calling process_inline(), so **bold** markers appeared as raw text. The fix is to call process_inline(c) on all non-header cell content.
Cover image: picsum.photos seeds produce irrelevant images
Using picsum.photos/seed/<word>/900/383 does NOT guarantee the seed word relates to the image content. Seeds like datalake, technology, database all produce random photos (vegetables, landscapes, etc.).
Better approach:
- Use a known-good tech image URL (e.g., from Pixabay/Unsplash with direct link)
- If needed, resize with
ffmpeg -i input.jpg -vf "scale=900:383" output.jpg - Or generate with
image_generatetool when available
Feishu /` blocks need manual preprocessing
normalize_for_wechat.py handles <callout>, <whiteboard>, <title>, <p> blocks — but not <grid> / <column> layout blocks that Feishu exports for multi-column content (e.g. pros/cons comparison).
Preprocessing pattern:
import re
grid_re = re.compile(r'<grid>(.*?)</grid>', re.S | re.I)
col_re = re.compile(r'<column[^>]*>(.*?)</column>', re.S | re.I)
def replace_grid(m):
cols = col_re.findall(m.group(1))
return "\n\n---\n\n".join(c.strip() for c in cols) + "\n" if cols else m.group(1)
text = grid_re.sub(replace_grid, text)
Run this before normalize_for_wechat.py.
Markdown tables MUST start and end with |
html_converter.py detects table rows via stripped.startswith("|"). When converting Feishu docs or writing markdown manually, every table row (including the header and separator) must start and end with |:
|列A|列B|列C|
|---|---|---|
|值1|值2|值3|
Without leading |, rows render as plain paragraphs. Use a fixer script:
# fix_table_pipes.py — add missing leading/trailing | to table rows
import re
lines = content.split('\n')
for i, line in enumerate(lines):
if '|' in line and not line.strip().startswith('```'):
if line.count('|') >= 2:
if not line.strip().startswith('|'):
lines[i] = '|' + line
if not line.strip().endswith('|'):
lines[i] = lines[i] + '|'
flush_table() must call process_inline() on cell contents
The original html_converter.py passed raw cell text into <td>/<th> tags without inline markdown processing. **bold** in table cells rendered as literal asterisks.
Fix applied (2026-07-01): flush_table() now calls process_inline(c) for both <th> and <td> cells.
Table headers: strip **bold** to avoid yellow highlight conflict
When process_inline() processes **text**, it applies the strong style which includes background: #fff18a (yellow highlight). In table headers (<th> with background: #0b1530; color: #ffffff), this yellow highlight makes text unreadable.
Fix applied (2026-07-01): Table header cells use clean_th_text() that strips **bold** markers before process_inline(), since <th> is already bold by default.
def flush_table():
...
if i == 0: # header row
def clean_th_text(t):
t = re.sub(r'\*\*([^*]+)\*\*', r'\1', t)
return process_inline(t)
cells_html = "".join(f'<th ...>{clean_th_text(c)}</th>' for c in cells)
else: # body rows
cells_html = "".join(f'<td ...>{process_inline(c)}</td>' for c in cells)
Cover image generation: use the skill's built-in pipeline (FIRST CHOICE)
⚠️ CRITICAL ORDER: Always try generate_image.py FIRST.
Do NOT try image_generate tool (FAL), picsum, pixabay, or any external image source before checking the skill's own generate_image.py script. The script reads API credentials directly from wechat-publisher.yaml and works even when image_generate tool reports missing keys.
Correct cover image priority:
- User-supplied cover image
generate_image.pyscript (reads config fromwechat-publisher.yaml)- If that configured provider/model is unavailable, use the active
image_generatetool when available - Article inline images (reuse the most representative one)
- Branded placeholder template
- Last resort: stock photo (warn user it's temporary)
After generation, always inspect the image visually, then inspect the actual file dimensions before resizing or cropping. Do not trust a tool's reported nominal size: backends may return a different aspect ratio. For WeChat covers, verify the final 900×383 artifact again after conversion.
Do NOT jump to picsum/pixabay/random images just because image_generate tool reports FAL_KEY missing — the skill has its own image generation backend configured in wechat-publisher.yaml.
Sensitive info masking — do NOT assume values are truncated
Hermes tools (read_file, terminal output, session_search) automatically mask sensitive values like API keys. A displayed value like sk-701...2b99 is NOT truncated — it is the full key being masked at the display layer for security.
Do NOT:
- Assume the key in the config file is broken or incomplete
- Ask the user to "provide the full key" when it's already there
- Waste rounds trying to "fix" a value that is already correct
Do:
- Just call the script that reads the config directly (
generate_image.py,wp-token.sh, etc.) — they read the actual file, not the masked display - If the script fails with 401, then investigate the key — but trust the file, not the display
Do not infer that a credential file is incomplete from masked tool output. Let the local script read the file directly, then diagnose only from the real API response.
The skill has a complete image generation pipeline:
generate_image.py— unified entry point, readswechat-publisher.yamlopenai_image_gen.py— OpenAI-compatible API backend- Config keys in
wechat-publisher.yaml:image_generation.generator(set to"openai")image_generation.openai.api_keyimage_generation.openai.base_urlimage_generation.openai.image_model
Usage:
cd scripts/wechat_publisher
python3 generate_image.py --generator openai \
-p "描述封面图内容的详细prompt" \
--image /tmp/cover.jpg \
--size "1792x1024" \
--quality "high"
Pitfall: If the API key in wechat-publisher.yaml is truncated or invalid, the script will return 401. Always verify the key is complete before attempting generation. If generation fails, report the error clearly and ask the user to provide the correct key — do NOT silently fall back to unrelated stock images.
--html mode cover behavior
publish.py --html <gzh-fragment> always requires --title. --cover is optional only when the HTML contains a resolvable first image, which the publisher can reuse as cover; otherwise provide a real cover explicitly. Never download a random Picsum image silently.
⚠️ Lorem Picsum random images are often irrelevant — seeds like database, technology return random photos (vegetables, landscapes, architecture) that have nothing to do with the topic. Random Picsum is acceptable only as a "won't crash" fallback. When the user sees an irrelevant cover, they will complain.
Better fallback strategies (in priority order):
- Ask the user to supply a cover image
- If the article has inline images, reuse the most representative one
- Generate a cover via the configured image backend (OpenAI / Gemini)
- Use a solid-color SVG or a simple branded template image
- Last resort: Picsum random — but warn the user it's a placeholder
html_converter.py table cells must call process_inline() for markdown rendering
The flush_table() function in html_converter.py originally inserted table cell content raw — without calling process_inline(). This meant **bold**, [links](url), and other inline markdown inside table cells rendered as literal text.
Fix applied (2026-07): Changed flush_table() to call process_inline() on every cell:
# Before (broken):
cells_html = "".join(f'<th style="...">{c}</th>' for c in cells)
# After (fixed):
cells_html = "".join(f'<th style="...">{process_inline(c)}</th>' for c in cells)
Same for <td> cells. The patch is applied to the skill's copy of html_converter.py. If you replace or update the file, verify this fix is present.
Validation: after conversion, check the HTML output — if **text** appears as literal asterisks inside <td> or <th> tags, the fix was lost.
Mermaid / flowchart code blocks must be rendered to images before gzh-design
Before publishing any article containing Mermaid blocks:
- search the normalized Markdown for fenced
mermaidblocks orflowchart/mindmapsyntax; - render each diagram to a PNG;
- replace the fenced block with
; - render the resulting Markdown with gzh-design and verify no Mermaid source remains in the HTML;
- publish the clean HTML through
wp-publish.sh; it uploads the local image source to WeChat CDN.
If the server lacks Chinese fonts, download/use a local CJK font and reference it via @font-face when rendering diagrams with Playwright; otherwise Chinese labels become square glyphs in the generated image.
html_converter.py requires tables to start and end with |
The table detection in html_converter.py uses this check:
if "|" in stripped and stripped.startswith("|"):
Markdown tables that lack leading/trailing | (e.g. **维度** | **传统** | **多模态** instead of |**维度** | **传统** | **多模态**|) will not be detected as tables — they pass through as plain <p> text.
This commonly happens when:
- Feishu doc exports produce bare pipe-separated rows without
|boundaries - Content is hand-written with
|as separator but no leading|
Fix: normalize table rows before normalize_for_wechat.py:
import re
def fix_table_pipes(text: str) -> str:
"""Ensure markdown table rows start and end with |."""
lines = text.split('\n')
result = []
for line in lines:
if '|' in line and not line.strip().startswith('```'):
if line.count('|') >= 2:
if not line.strip().startswith('|'):
line = '|' + line
if not line.strip().endswith('|'):
line = line + '|'
result.append(line)
return '\n'.join(result)
This fix is available as scripts/fix_table_pipes.py.
Validation: after conversion, grep the HTML for <table — if absent and the source had tables, the pipe fix was missed.
Use the skill's wrapper scripts for operations:
scripts/wp-list-accounts.shscripts/wp-token.shscripts/wp-html.shscripts/wp-ai-score.shscripts/wp-publish.shscripts/wp-bootstrap.shscripts/wp-run.sh
Cover image generation
When image_generate tool is unavailable (no FAL_KEY), use the skill's built-in generate_image.py script:
cd ~/.hermes/skills/social-media/wechat-autopublish/scripts/wechat_publisher
python3 generate_image.py --generator openai \
-p "A modern data lake architecture diagram..." \
--image /tmp/cover.jpg \
--size "1792x1024" --quality "high"
The script reads API config from wechat-publisher.yaml (image_generation.openai.*).
If the API key is truncated or invalid, the script will fail with 401 — check the key before retrying.
When image generation is unavailable (no API key, API errors), fall back to a stock image from pixabay/picsum. Always inform the user about the fallback.
Image-generation integration notes are in references/wechat-image-generation.md.
Rendering, integration shape, and next-upgrade research are in references/wechat-rendering-and-integration-notes.md.
WeChat justified text stretches Chinese spacing
Do not use text-align: justify for normal WeChat article paragraphs. In WeChat preview/WebView, Chinese-English mixed lines can be expanded aggressively, creating large gaps between Chinese characters and English terms. Use:
text-align: left;
letter-spacing: 0.15px;
word-spacing: 0;
This applies to the default converter styles and the refined-blue theme. Verify the actual draft HTML returned by draft/get, not only the pre-upload HTML.
WeChat sanitizes multi-line blockquotes
Do not render Feishu callouts as one <blockquote> joined by <br>. WeChat's draft sanitizer can keep only the first line, silently deleting the callout body. Render each callout as a styled <section> containing one <p> per source line. After draft creation/update, call draft/get and verify that every callout still contains its body text.
List rendering strategy
WeChat Official Account HTML does not reliably support nested list indentation via ul/ol/li + CSS.
Therefore:
- use a standard Markdown renderer for correctness
- then convert nested lists into WeChat-safe list components using
p+span - preserve indentation through explicit left padding
- preserve numbering/bullets through explicit styled spans
This avoids duplicated numbering and unstable nested-list rendering.
Preconditions
Before attempting draft-box publishing, confirm:
- the WeChat account is a personal subscription account
AppIDis availableAppSecretis available- the server/public IP is added to the WeChat IP whitelist
- the publishing toolchain is configured inside
scripts/wechat_publisher/wechat-publisher.yaml - image generation configuration is understood:
image_generation.openai.*is used for OpenAI-compatible image backends,image_generation.gemini_proxy.*for Gemini-style backends, andimage_generation.generatorselects the active path - if a generated cover is required, the active image backend is configured before publishing
If any precondition fails, fall back to the publishing package workflow rather than stopping completely.
Operational lesson from actual use:
- when the user asks how to configure YAML, answer in execution-project terms first
- clarify that secrets / provider keys / account credentials belong in the execution project config
- clarify whether image generation is currently wired into this skill's publishing flow before giving configuration advice
If any precondition fails, fall back to the publishing package workflow rather than stopping completely.
Core Principle
This skill should always separate content production from account-specific publishing mechanics.
A stronger publishing principle should also be enforced:
- all inputs must first be normalized into WeChat-ready Markdown
- Markdown → HTML is the single main rendering path
- source-specific preprocessing (Feishu docs, rich exports, mixed tags) belongs in the Markdown normalization layer, not inside the HTML renderer
- preserve source value where appropriate; do not blindly compress detailed source documents when the user wants publication-grade content
This keeps the publishing pipeline stable and makes the rendering core reusable across different input types.
Two primary operating modes should be supported explicitly:
Feishu doc publishing mode
- Use when the user gives a Feishu doc URL/token and wants it published as a WeChat article.
- The Feishu doc is treated as the main source of truth.
- The skill should extract, restructure, de-internalize, and convert the doc into publishable WeChat content.
- This mode should be used when the user wants a publishing-grade rewrite or restructure.
Copy-transfer mode
- Use when the user wants the source article transferred to WeChat as faithfully as possible.
- Prefer preserving original structure, detail density, figures, and appendices over aggressive rewriting.
- This mode is especially appropriate when the source article is already mature and the user says things like “搬运一下”“图也得搬一下”“不能太精简”.
- The goal is publishable fidelity, not fresh content reconstruction.
Topic content-building mode
- Use when the user gives only a topic, outline, or rough idea.
- The skill should build the article end-to-end: define the job, research, outline, draft, humanize, format, and package for publishing.
Support files for execution:
references/gzh-design-integration.md— canonical renderer contract and artifact boundaryreferences/wechat-publisher-execution.mdreferences/feishu-wechat-conversion.mdreferences/html-publishing-quality-gate.md— renderer failure signatures and the pre/post-publish verification gatetemplates/wechat-publisher.yaml.exampletemplates/feishu-doc-intake.mdtemplates/topic-intake.mdtemplates/final-package.md
Recommended abstraction:
- Source intake
- Research / evidence gathering
- Structuring
- Drafting
- Humanization / anti-AI flattening
- WeChat formatting
- Publishing execution via wechat-publisher
- Output packaging / fallback packaging
This skill should treat wechat-publisher as the execution backend, not duplicate its publishing logic.
Copy-transfer mode: Feishu doc → WeChat copy
Use this mode when the source article should remain close to the original, especially when the user wants the original detail and diagrams preserved.
Default behavior:
- preserve section hierarchy and detail density
- preserve figures, appendices, tables, code blocks, callouts, and examples
- perform only necessary publishing cleanup instead of fresh rewriting
- rewrite only where internal phrasing, internal context, or platform formatting needs adjustment
- export embedded Feishu whiteboard/diagram/media content as images when possible and include them in the article
Important copy-transfer rules:
- do not compress a detailed document into a summary just because it is long
- do not strip diagrams unless they truly cannot be recovered or published
- do not replace an already-strong structure with a generic article skeleton
- treat user signals like “搬运一下” and “图也得搬一下” as explicit instructions to preserve content volume and figures
- if the user criticizes a version as “太精简”, the next version must restore detail and figures first
Completion criteria:
- the published WeChat draft is a faithful transfer of the source article
- figures are migrated wherever technically possible
- the article retains the original depth unless the user explicitly asks for condensation
When the source is a Feishu document:
- load the document first
- identify the original goal, audience, and structure
- convert internal phrasing into public-facing WeChat phrasing where needed
- preserve technical accuracy, original names, diagrams or examples worth keeping, and practical substance
- do not over-summarize; reshape the document into a publishable article instead of reducing it to a shallow overview
Feishu conversion rules
When loading a Feishu document, the feishu_doc_read tool will fail with "Feishu client not available" outside of Feishu comment contexts. Reliable fallback:
~/.local/bin/lark-cli docs +fetch --doc <token-or-url> --doc-format markdown --format json -q '.data.document.content'
Note: The feishu_doc_read tool is only available when Hermes is running inside a Feishu gateway context. In all other contexts (terminal, cron jobs, Desktop), use the lark-cli fallback above.
- preserve the technical spine of the document, not the internal presentation order
- keep real components, real flows, real constraints, and real examples whenever they are valuable to an external reader
- remove internal assumptions that an outside reader cannot understand or does not care about
- rewrite internal system names, internal project references, or internal shorthand unless they are essential to the story
- convert note-like or memo-like structure into a deliberate article structure
- if the document is too linear, too flat, or too internal, rebuild it into a stronger skeleton before drafting
- avoid flattening the document into a summary; the goal is publishing reconstruction, not compression
- make the final article feel like the author deliberately wrote it for WeChat readers, not like a sanitized internal export
- if the source Feishu doc contains
<callout>blocks, convert them explicitly to WeChat-safe structures before HTML conversion; do not leave raw Feishu-specific tags in the publishing payload - if the source Feishu doc contains embedded whiteboard/export figures, migrate them explicitly as images and deduplicate references before publishing
Feishu structural rebuilding heuristics
Rebuild the skeleton when the source doc shows any of these patterns:
- the document is essentially raw notes or bullet-heavy internal thinking
- the structure is too chronological without a clear publishing payoff
- the internal ordering buries the most publishable points
- the document mixes audience types or mixes unrelated concerns
- the original structure would make a WeChat reader drop off early
Preferred rebuilt shapes:
- problem → background → core approach → practical examples → pitfalls → conclusion
- objective → design/workflow → implementation notes → lessons → next steps
- question/context → architecture → execution details → verification → recommendation
Completion criteria:
- the Feishu doc has been processed as primary source material
- internal phrasing has been converted where needed without destroying technical value
- the final article reads like an intentional WeChat article rather than a raw internal doc repost
Topic → content building
When the source is only a topic:
- define the publishing job before generating content
- search for current and reliable material only as much as needed
- choose an article structure deliberately
- build a skeleton before long-form writing
- draft the article with real technical substance
- then humanize and package it
Cover image prompt strategy
When generating cover images, the prompt must extract specific structural information from the article content, not use generic abstract descriptions.
Bad prompt (generic):
"A minimalist technical architecture diagram cover image. Dark navy blue background with abstract glowing network nodes..."
Good prompt (content-specific):
Extract the article's core structure — layer names, component names, design pillars, key concepts — and describe them explicitly in the prompt. For example, for an architecture article, describe the actual layers (e.g., "Layer 1 Multi-Entry with CLI/Web/API, Layer 2 Core Runtime with Session/Scheduler/Executor"), the title text, subtitle, color scheme per layer, and layout.
The cover image generation pipeline:
- read the article content
- extract key structural elements (title, layers, components, design pillars)
- build a detailed prompt that describes these elements visually
- call
generate_image.py --generator openai - verify the generated image visually before publishing
Image generation strategy
This skill should treat image generation as a configurable publishing capability, not as a hard requirement.
Current integration status:
- the
wechat-publisherexecution project has image generation support and config keys - this skill has wired execution scripts for publishing / HTML conversion / AI-score checking
- image generation is not yet embedded as a required default step in this skill's publishing loop
Default recommendation:
- publishing should succeed even when image generation is disabled or unconfigured
- if image generation is unavailable, fall back to text-first layout or manual cover selection
- if image generation is enabled, separate the flows clearly:
- cover image
- in-article images
- fallback behavior when image generation partially fails
When advising on YAML configuration, clarify these distinct blocks:
- account credentials
- publishing behavior
- image generation provider keys / model settings
- integration tokens
This prevents confusion between "what is needed to publish" and "what is needed to auto-generate images".
Topic selection rules
Prefer these topic angles for the owner's personal brand:
- practical workflow / practice sharing
- architecture or system design breakdown
- toolchain setup, evaluation, or comparison
- engineering pitfalls and debugging notes
- open-source project analysis from a practitioner view
- data infrastructure, data integration, data synchronization, or adjacent engineering topics
Avoid these angles unless explicitly requested:
- shallow news reposting without analysis
- motivational or generic industry commentary
- purely promotional tool introduction without practitioner value
Topic-building quality checks
Before drafting, confirm the chosen structure answers these questions:
- what is the article actually giving the reader?
- why is this structure the right one for the topic?
- where are the concrete examples or practitioner judgments?
- what will the reader be able to do after reading the article?
Completion criteria:
- the topic has been shaped into a clear article job
- the output follows a deliberate structure rather than generic expansion
Workflow
Step 1: Clarify the source type
The skill should first identify the input type:
- topic only
- Feishu document
- Markdown / notes
- reference links
- existing draft
Completion criteria:
- source type is identified
- publishing account is confirmed
- intent is clear: publish now, produce draft, or produce review package
If the source is a Feishu doc, load it directly with Feishu doc tools when possible and treat it as the primary source of truth. If the source is only a topic, confirm the minimum missing context before research.
Step 2: Define the article job
Before writing, define:
- target reader
- article goal
- article type
- expected structure
Common article types:
- practical tutorial
- architecture / workflow explanation
- toolchain evaluation
- troubleshooting / case study
- industry or technical trend analysis
- document-to-article conversion
Completion criteria:
- the article job is written in one short paragraph
- the chosen structure is explicit
Step 3: Collect research material
Do research only after the job is defined.
Gather:
- official docs / release notes / blog posts
- real commands, configs, APIs, and behaviors
- high-quality recent references
- concrete examples or deployment notes
- only relevant discussion when it adds practitioner perspective
Do not pad research with generic background.
Completion criteria:
- key facts are collected
- sources are available
- reusable examples are identified
Step 4: Build the skeleton
Before full drafting, generate a skeleton:
- title candidates
- summary / digest
- section outline
- per-section angle
- expected diagrams / examples
- cover concept
Prefer skeletons with:
- a sharp opening
- 3-6 sections
- uneven section length if needed
- a clear ending that adds judgment or next-step advice
Completion criteria:
- skeleton exists and can be reviewed before long-form writing
Step 5: Draft the
…(truncated)