wjs-publishing-wechat
Help users write WeChat Official Account articles. Light polishing, no rewriting. Automatically generate cover images and explanatory illustrations, and output content packages that can be directly pasted into the WeChat Official Account backend.
Core Principle
Preserve the author's tone and rhythm. The user's ideas and expression style are the soul of the article. You only need to do four things:
- Correct obvious typos and repeated characters
- Adjust paragraphs (WeChat readers prefer short paragraphs, 1–3 sentences per paragraph)
- Smooth out particularly awkward sentences (conservative, avoid changes if possible)
- Prepare supporting materials (cover image, title candidates, abstract)
Things NOT to do:
- Do not change the author's word preference
- Do not add AI-style connecting words ("First", "Second", "In summary", "All in all", "It is worth noting that")
- Do not convert spoken language to written language
- Do not add emojis (unless included in the original text)
- Do not reorganize paragraph order
- Do not "improve" the author's expression — let them write as they wish, you are just a cleaner
Length and Examples (Hard Constraints)
Default length: 800–1000 words. 2000 words is considered overly long, and only allowed for special reasons (series articles, in-depth technical articles). Write according to this word count from the first draft, do not cut down after writing — articles written to the word count have tight rhythm, while cut-down articles will have a spliced feel.
When writing example paragraphs, check against this rule:
Is this example truly specific (real event / real person / real numbers / real details), or made up just to demonstrate a framework?
Delete the latter and let
take on the function of "demonstrating structure" — the structure diagram already shows the entire process, and expanding the same structure in text in the main body is redundant and empty content.
Prioritize retaining: Opening contrast/hook + core framework + 1 key sentence + soft landing conclusion.
Prioritize cutting: Demonstrative examples, repeated explanations, instructional paragraphs like "how to use / where to find the entrance" (let the README of the introduced tool/skill explain this).
Do not write by default. The article should conclude at the end of the main body. Add it only when
truly necessary for acknowledgments, source citations, or important supplements to the previous content (not for padding) — such as acknowledging the original author when paraphrasing others' frameworks. Sign-offs like "correct me if I am wrong — welcome feedback" do not justify adding a postscript.
Be sure to count the word count after writing. Use this command:
python3 -c "import re; t=open('article.md').read(); t=re.sub(r'\!\[.*?\]\(.*?\)','',t); print(len(re.findall(r'[一-鿿]',t)) + len(re.findall(r'[A-Za-z]+',t)))"
. If it exceeds 1200 words, go back and cut again.
Articles Introducing Skills: Must Include 5 Platform Installation Methods at the End
Trigger condition: This article introduces/recommends/explains a specific Claude Code skill (whether written by Wang Jianshuo or others).
Precondition — Confirm the skill is published:
- Wang Jianshuo's skills: After writing SKILL.md,
~/.claude/skills-publish-hook.sh
will automatically rsync + commit + push to github.com/jianshuo/claude-skills, no manual action needed. Use gh api repos/jianshuo/claude-skills/contents/<skill-name>
to confirm it is online
- Others' skills: Confirm it is in a public git repo before writing the article. If not, ask the author to publish it first, so readers can install it
Must include the following section at the end (directly replace
with the actual skill name):
markdown
## Installation Methods
No need to copy commands. Open your AI agent — Claude Code, Codex, Kimi Code, OpenClaw all work — and say to it:
> Install https://github.com/jianshuo/claude-skills/blob/main/<SKILL_NAME>/SKILL.md
It will automatically fetch it, place it in the skill directory of its platform, and prompt you to restart the conversation.
For Hermes, use the command line directly:
```bash
hermes skills install https://github.com/jianshuo/claude-skills/blob/main/<SKILL_NAME>/SKILL.md
After installation, say to the agent "<the most natural trigger phrase closely related to this skill's entry>" to use it.
**Rules**:
1. This section **does not count** towards the 800–1000 word budget (it is appendix tool information, not main body content)
2. **Why "tell the agent a sentence" instead of `cp -r` command**: In the agent era, installation = letting the agent fetch the URL and write it to its platform's skill directory. Any agent with internet access can handle this, no need for users to remember the directory path of each platform. The `cp -r` command is too technical for WeChat Official Account readers and belongs to the previous era of installation methods
3. Use the `github.com/<owner>/<repo>/blob/main/<path>` format for URLs: It can be directly viewed in the browser (readers can check it before deciding to install), and LLM agents can automatically extract the markdown content from the blob URL (no need to manually convert to raw.githubusercontent.com)
4. List the **command line** form separately for Hermes: Because Hermes is a skill registry CLI rather than a chat agent, it has no "tell it a sentence" entry, but its `hermes skills install <URL>` accepts the same URL and is the cleanest equivalent
5. The final sentence "After installation, say to the agent '...'" should be written according to the actual trigger phrase of the current skill. For example, "I want to learn from my mistakes, about this recent incident——" / "Help me prepare an official account article". **Do not omit this sentence** — if readers don't know how to start using it after installation, the entire installation section is useless
6. Usually placed at the end of the article (no `## Postscript` by default); if the article exceptionally has `## Postscript`, place the installation method before the postscript
7. If new agent platforms supporting SKILL.md emerge in the future, **just add the name to the first paragraph's platform list** ("Claude Code, Codex, Kimi Code, OpenClaw, New Platform all work"), no need to write a new command line for it
## When This Skill Fires
- The user provides a set of ideas, a draft, or speech-to-text text
- The user says "Help me write an official account article", "Polish this", "Prepare for publication"
- The user works in the WeChat publication working directory (default `~/wechat-publish/` or `~/code/wechat-publish/`, configurable by the user)
## Workflow
### Step 0: Receive Input
Users will provide content in the following forms:
- Complete draft (most common)
- Several scattered ideas / bullet points
- A long text without paragraph breaks
- Speech-to-text (may have typos, repetitions)
If the input is too scattered, **ask one question**: "Is this intended to be an article, or several independent ideas?" — but only ask once.
### Step 1: Light Polishing
Open a markdown file, paste the user's content into it. Then **only** do the following:
- Correct typos (misuse of "的/得/地", homophone errors, repeated characters like "我我")
- Split paragraphs: 1–3 sentences per paragraph. Long paragraphs are hard to read in WeChat
- Make minimal changes to awkward parts. If the tone changes after modification, prefer not to change
- Unify punctuation: Use full-width commas and periods for Chinese, add spaces between English/numbers
- Keep the original opening and conclusion — these are the author's signature features
**Modification scale reference**: If the number of words you modify exceeds 5% of the original text, you have modified too much. Go back and adjust.
### Step 2: Title Candidates
Provide the user with **3 title candidates**:
- A) Straightforward type: Directly state what the article is about
- B) Story type: Start with a scenario or conflict
- C) A sentence from the user's original text: Extract the most vivid sentence from the draft
Do not: Use clickbait, exaggeration, "Shocking", "Must-read".
### Step 3: Abstract (50–80 words)
The WeChat Official Account abstract is the preview when shared to Moments/dialog boxes. Key points:
- Not a copy of the first paragraph of the article
- Clearly state what readers will gain in one sentence
- Use the author's tone, not marketing language
### Step 4: Images (Two per Article)
Each article is paired with **two images**:
- **Cover image cover.png** — The cover before entering the article, **strictly 2.35:1** (900×383, i.e., 900÷383=2.349), for the cover field in the WeChat editor. Strong fonts, strong composition, text-driven
- **Explanatory illustration illustration.png** — The image in the main body, **ratio determined by content** (model-selected), helps readers understand the core structure of the article at a glance. Flat cartoon style, with labels and processes
**Cover images are generated by AI by default** (no need to ask the user, each costs about $0.05–0.20):
```bash
~/.claude/skills/wjs-publishing-wechat/scripts/gen-cover-ai.sh <article-folder> ["target words"]
- If the second parameter is not passed, take from as the target words
- Internally calls , forces the use of (no longer supports OpenAI API key fallback)
- Default size (the landscape size closest to 2.35:1), automatically cropped to 900×383 with sips centered
- The original image is saved as , and the cropped version is
- is used as (design philosophy), and the short generation instruction is used as — this way, gpt-5.4 can digest the long prompt before calling the image_generation tool
- Adjustable environment variables:
WECHAT_PUBLISH_IMAGE_SIZE
(default ), WECHAT_PUBLISH_IMAGE_QUALITY
(default )
Pre-dependencies: Must have
installed:
bash
git clone https://github.com/Wangnov/gpt-image-2-skill /tmp/g
cp -r /tmp/g/skills/gpt-image-2-skill ~/.claude/skills/
And must have Codex authentication:
- Only supported: Codex (ChatGPT Plus plan is sufficient, no OpenAI organization verification required, gpt-image-2 renders Chinese characters significantly more accurately than gpt-image-1)
- No longer supported: Direct connection via (only the Codex provider supports , and the API mode bypasses Codex's prompt optimization)
Selection of target words: Article titles are often long phrases (e.g., "Three Simple Levels of AI Capabilities"), but the prompt template is more friendly to single characters/2-character words. You can suggest the user select core concept words:
What target words to use? The default is the article title. It is recommended to select a core concept word (1–4 characters), for example, "Three Simple Levels of AI Capabilities" can use "Three Levels" or "Levels".
Then generate the explanatory illustration(no need to ask the user, run automatically):
bash
~/.claude/skills/wjs-publishing-wechat/scripts/gen-illustration.sh <article-folder>
- Reads the full text of and passes it as instructions to gpt-image-2
- After the model understands the core structure of the article, it generates a flat cartoon explanatory illustration
- No cropping, the model selects the frame size (two-line comparison usually uses 3:2, process types use horizontal long strips, hierarchical depth uses vertical version)
- Outputs , directly used as the image in the main body
Important: The explanatory illustration must be referenced in the markdown — there must be a line
in
, otherwise the draft uploaded by
will not show this image (even though the image has been uploaded to the CDN).
Default insertion position:
After the conclusion of the main body (no postscript by default; if there is
, place it before the postscript; if there is
, place it before the installation methods) — treat the explanatory illustration as a visual summary of "this is what the whole thing looks like", with a colloquial guide (e.g., "When you draw the whole thing, it looks roughly like this:"), do not write explanatory language like "As shown in the figure". If the explanatory illustration only targets a certain section (not a full-text summary), place it immediately after the main body of that section.
Safety net: If
is generated but the reference line is missing in
,
will automatically insert the reference in the most appropriate position (before the postscript if there is one, otherwise at the end of the article) and rewrite
to ensure idempotent results. But it is preferred to place it when writing
in Step 5.
If the user is not satisfied with a certain image, directly re-run the corresponding script — the result will be different each time.
Step 5: Output File Package
Create a folder in the user's working directory (default
~/wechat-publish/articles/
):
articles/2026-05-09-{slug}/
├── article.md # Polished markdown source file
├── article.html # Converted to HTML, ready for direct pasting
├── cover.png # Cover image 900×383 (strict 2.35:1)
├── illustration.png # Explanatory illustration (any ratio, model-selected)
├── meta.json # { title, summary, author, date, slug }
└── original.md # Backup of user's original input
is generated from the title: Pinyin initials + keywords, limited to 30 characters or less. For example, "My First Mac" →
.
article.html conversion rules:
- Use or simple markdown parsing (no complex styles needed, the WeChat editor will reformat it)
- Keep paragraph breaks ()
- Keep bold () and lists
- Do not use inline CSS — the WeChat editor will clear it
bash
pandoc article.md -f markdown -t html -o article.html
# If pandoc is not available:
# Use Python's markdown package / Node's marked / or write a minimal implementation
Step 6: Publish (Use with md2wechat under the hood)
Once the article package is ready, run one command to push the article as a draft to the WeChat Official Account backend:
bash
~/.claude/skills/wjs-publishing-wechat/scripts/upload-draft.sh \
<workspace>/articles/YYYY-MM-DD-{slug}
The script does 4 things internally (uses low-level commands of
, bypasses the API key restriction of its high-level
command):
md2wechat upload_image cover.png
→ Get
- If exists but is not referenced in : Automatically insert

in the most appropriate position (before if there is one, otherwise at the end of the article) and rewrite (idempotent safety net). Then md2wechat upload_image illustration.png
→ Get WeChat CDN
- Generate from :
- Remove frontmatter and H1 in the main body (avoid DUPLICATE_H1 warning from md2wechat inspect)
- Supported markdown blocks: / / / / / / / / / / (markdown pipe table)
- Raw HTML block passthrough: Blocks starting with (typical use case:
<section style="background:#f7f5f0;…">…</section>
wrapping a quote/comment card with light background + gray text) will be output as-is, not wrapped in . The entire block must be one block — no empty lines inside to break it, otherwise it will be split. The author is responsible for ensuring the HTML is valid and compatible with the WeChat editor
- Do not write inline CSS for paragraphs and images — let the WeChat editor's default line-height / font-size / color take over
- Use as spacing between paragraphs (consistent with the source code of manually pressing Enter twice in the editor; cannot be omitted, otherwise adjacent will stick together without margin; also cannot use or empty , which will be normalized and removed by the editor)
- Multi-line within a paragraph → automatically line breaks: If a markdown paragraph (no empty lines between lines) has multiple lines, each is replaced with during HTML conversion, and the entire paragraph remains in one . Specifically designed for parallel/coordinate short phrases written as "one sentence per line visually, but belonging to the same paragraph" — refer to "Parallel/coordinate short phrases should be written in separate lines, not separate paragraphs" in [[wangjianshuo-perspective]]. bold can also span these lines (inline regex uses instead of as the internal boundary)
- Structural style exceptions (these inline styles must be added, otherwise readability will be damaged):
- :
font-size:1.4em; font-weight:bold;
(two sizes larger than the main body + bold)
- :
font-size:1.2em; font-weight:bold;
(one size larger than the main body + bold)
- (i.e., ): (pure red bold — a visual point intentionally set by the author. Only applies to converted from markdown ; manually written with explicit inline style in raw HTML blocks will not be overwritten)
- :
border-collapse:collapse; width:100%;
- :
border:1px solid #d9d9d9; padding:6px 10px;
( additionally has )
- :
font-family:Menlo,Consolas,monospace; background:#f4f4f4; padding:1px 6px; border-radius:3px; font-size:0.92em;
(without this, commands and ordinary text are mixed and indistinguishable)
- Judgment principle: Decorative styles (line height, color, font) are handled by the WeChat editor; structural styles (title hierarchy, table borders, code visual blocks) must be inline — without them, they will degenerate into main body text / several lines of bare text, losing the meaning of the block
- Fenced code block (): The script automatically strips the ``` fences and language names ("bash" / "python" etc.), converts to , and connects multiple lines with . Do not use — the WeChat editor is not friendly to blocks, and short commands using inline-styled look cleaner visually
- Preferred way to display commands: Use inline (a pair of backticks wrapping the command) directly in article.md, instead of fenced ```bash blocks. Unless there is really a multi-line continuous shell process that must use blocks, inline is more suitable for WeChat Official Account reading
- Replace with the CDN URL
- Assemble from
- Draft writing / updating:
- If already exists in (indicating this article was previously sent as a draft), first run to reuse the same media_id for in-place update in the backend — the WeChat backend has built-in version control, no new draft will be generated; only the timestamp will be refreshed
- If the old media_id was deleted by the user in the backend → API returns , the script automatically falls back to to create a new draft
- To force creation of a new draft (e.g., to keep the old version for comparison):
export WECHAT_PUBLISH_FORCE_NEW=1
- This "priority update" was added in May 2026; previously, a new draft was created every time — repeated revisions of the same article would pollute the draft box
Why write our own update? The md2wechat CLI only exposes
, but the WeChat backend API has
(this URL can be grep'ed in the md2wechat binary, but it is not exposed). So this skill uses 30 lines of Python to call it directly, bypassing the md2wechat CLI layer.
- Uses the of the current shell (necessary! The WeChat IP whitelist recognizes the proxy exit IP, not the local direct IP)
- Automatically falls back on old ; other errors (e.g., 45004 description too long) are directly bubbled up, consistent with the creation path
Pre-dependencies:
- CLI is installed and configured with + (verify with )
- Current public IP has been added to the WeChat Official Account backend whitelist: mp.weixin.qq.com → Settings and Development → Basic Configuration → IP Whitelist. Omitting this step will return , and adding to the whitelist takes effect in tens of seconds
- For detailed commands, provider selection, and brand profiles, refer to the skill
Why not use md2wechat convert --draft
? It was found through actual testing that this "one-click" path does not work under default configurations:
- (default) requires (paid cloud rendering service from md2wechat.cn), which ordinary users do not have
- does not directly output HTML, but returns a prompt for external AI rendering, which is not closed-loop
So this skill combines the two low-level commands
+
to assemble HTML and draft JSON by itself.
encapsulates this process into one command.
Step 6.1 — Optional: Inspect / Preview First
bash
cd <workspace>/articles/YYYY-MM-DD-{slug}
md2wechat inspect article.md # Check metadata, word count, publication readiness status
md2wechat preview article.md # Generate local HTML preview (degraded mode, can get a rough idea)
If you want to confirm whether metadata is too long or the abstract is empty before publishing, run
. Otherwise, skip directly to 6.2.
Step 6.2 — One-Command Publication
bash
~/.claude/skills/wjs-publishing-wechat/scripts/upload-draft.sh \
/Users/jianshuo/code/wechat-publish/articles/YYYY-MM-DD-{slug}
Upon success, it outputs the
, and leaves two products
and
in the article directory for review or re-publishing directly with
md2wechat create_draft draft.json
next time.
Step 6.3 — Backend Preview and Publication
After
succeeds,
it will automatically open https://mp.weixin.qq.com/
with the default browser (uses
on macOS,
on Linux). If the browser is already logged in, it will directly enter the homepage, and you can click "Draft Box" to see the draft just uploaded.
To disable auto-open (e.g., avoid opening multiple tabs when running multiple articles in batches):
export WECHAT_PUBLISH_NO_OPEN=1
.
Note: The
precise edit deep link URL of the draft is like
…appmsg_edit_v2?action=edit&appmsgid=XXX&token=YYY&…
, but
is the internal ID of the backend database (not equal to the
returned by the API), and
is session-bound, so
it is impossible to assemble the deep link from the API return value. Opening the draft box for the user to select is the most stable thing that can be done currently.
Go to the draft box → find the article just uploaded → preview on mobile → publish.
If errors occur:
errcode=40164 not in whitelist
: Add the current public IP to the WeChat MP backend whitelist
- : The in is empty or too short
- Cover-related: Confirm the path is correct and the size is ≥ 900×383
- Token / appid: Check the configuration with
md2wechat config validate
Optional — Advanced Typesetting: If modules like first-screen judgment, CTA, author business card are needed, add
syntax in
(requires
to render). This skill does not add it by default to keep the author's original text clean.
Step 7 (Optional) — API Mass Sending + Fetch Comments
⚠️ Policy Change in July 2025 — API Publishing Rights for Personal Subject Accounts Revoked
Since July 2025, WeChat has revoked the API call permissions for "publishing capabilities" of
personal subject authenticated accounts (even with yellow V).
/
/
/
all return
.
Only "enterprise subject authenticated" (authenticated with company business license) service accounts/subscription accounts have API publishing + comment fetching permissions.
Judgment method: Try calling
once. Returns 48001 → personal subject (this entire Step 7 is unavailable, skip to Step 8 for cookie fallback). Returns 0 → enterprise subject, all processes below are available.
If your account is blocked by 48001: Use
Step 8 - Cookie-based Comment Fetching to replace this section.
Precondition: The WeChat Official Account is an enterprise subject authenticated subscription account or service account. Personal subject authentication (yellow V) is not acceptable — must be a company subject.
Why design it as two separate commands instead of integrating into :
- Subscription accounts have only 1 API mass sending quota per day, and an automatic trigger error will waste it
- Mass sending skips the manual check step of "preview in draft box backend → correct typos" — retain this step
What the two commands do respectively:
mass-send.sh <folder> --preview <my-openid>
- Calls
cgi-bin/message/mass/preview
to send the created draft to your own WeChat
- Does not consume the daily mass sending quota, specifically used for "one last look at the actual effect before mass sending"
- Run only after confirming it is OK in WeChat
mass-send.sh <folder> --send
- Calls
cgi-bin/message/mass/sendall
() to truly send to all followers
- After success, automatically calls to enable the comment function for this article (not enabling it will return when fetching comments)
- Writes + back to , and the downstream finds this article based on this
fetch-comments.sh <folder> [--md|--json|--both]
- Reads from
- Fetches all comments by pagination ( returns 50 items per page, automatically paginates until complete)
- By default outputs Markdown to (includes nickname prefix, time, featured mark, like count, public reply); outputs the original API payload to
Typical process (authenticated accounts):
upload-draft.sh <folder> # Create draft + write publish.json
mass-send.sh <folder> --preview <my-openid> # Check it on your mobile phone
mass-send.sh <folder> --send # Truly mass send + enable comments
# Wait a few minutes to hours for followers to see it + leave comments
fetch-comments.sh <folder> # comments.md is generated
publish.json fields (incremental writing, never lose previous fields):
- / — written by
- / / — written by
- / / / — written by
When not to use this path:
- Unauthenticated accounts (48001): Get personal authentication first
- Already manually mass sent this article via the backend: cannot be obtained, only manually check/export comments via the mp.weixin.qq.com backend comment management
- Sent via permanent link: Not considered "mass sending", does not appear in historical messages, cannot be obtained — also can only check via the backend
Common errcodes:
- — api unauthorized: The most common reason is not lack of authentication, but WeChat revoked API publishing rights for personal subjects in July 2025 (see the note at the top of this section). Switch to an enterprise subject or use Step 8 cookie fallback
- — Daily mass sending quota exhausted
- — Comments not enabled ( will automatically ; if you skip --send and directly fetch comments, you will hit this)
Step 8 (Optional) — Cookie Fallback: Fetch Comments (Only Available Path for Personal Subjects)
Applicable scenario: The WeChat Official Account is personal subject authenticated (the official API path in Step 7 is blocked by 48001), but you still want to programmatically fetch comments.
Principle: The mp.weixin.qq.com backend is an SPA, and all comment data is returned as JSON via the internal endpoint
mp.weixin.qq.com/misc/appmsgcomment?action=...&token=...&begin=...&count=...
. You can make it work by bringing the cookie from the backend login + the session token in the URL.
Precondition: Logged in to mp.weixin.qq.com in the browser. Choose one of the two paths:
Path A (Recommended): Reuse gstack Persistent Browser — fetch-comments-via-gstack.sh
Original cookies expire in a few hours; but the login state in the Chromium profile maintained by gstack (
~/.gstack/chromium-profile/
) can actually last
3-14 days (community experience, planning for 7 days is more stable) — as long as you visit occasionally to keep it active. This path eliminates all manual packet capture steps.
Two strict rules:
- The gstack profile must exclusively use the mp.weixin.qq.com domain. Do not log in to the same WeChat Official Account in system Chrome / Safari / other browsers at the same time — concurrent login will invalidate the session on the gstack side, and you will have to scan the code again.
- Failure mode: When the session actually dies, the request will return an HTML login page (not JSON), and the script will report "non-JSON response → cookie expired, re-grab". At this point, run
browse goto https://mp.weixin.qq.com/
+ scan the code.
bash
# One-time setup (per machine): Scan code to log in to mp.weixin.qq.com in the gstack profile
~/.claude/skills/gstack/browse/dist/browse goto https://mp.weixin.qq.com/
# Scan the code with your mobile WeChat
# One-time setup (per article): Save the appmsgcomment URL template
# (The URL is stable per article; the script will read the latest token from the browser and replace it each time)
echo '<full appmsgcomment URL>' > <article-folder>/comment-url.txt
# Fetch comments every time afterwards (zero manual steps):
~/.claude/skills/wjs-publishing-wechat/scripts/fetch-comments-via-gstack.sh \
<article-folder> [--md|--json|--both]
Script process:
browse goto mp.weixin.qq.com/cgi-bin/home
— Refresh session + verify login
- — Capture the current
- — Get the full set of cookies, filter for the weixin.qq.com domain, convert to Cookie header
- Replace the token in with the latest one
- Call
fetch-comments-by-cookie.sh
(the script in Path B below) to complete the fetching
When it fails: The browser profile is logged out by WeChat (异地登录 / long-term non-use). At this point, the script will clearly tell you to re-run
browse goto https://mp.weixin.qq.com/
+ scan the code.
Path B (Fallback): Manually Capture Cookie — fetch-comments-by-cookie.sh
If gstack is not installed, or you want to fetch quickly once, you can directly use this path:
bash
# 1. Browser packet capture (one-time, takes a few minutes)
# a. Log in to mp.weixin.qq.com → Comment Management → Find the target article
# b. Open DevTools (Cmd+Opt+I) → Network tab → Filter Fetch/XHR
# c. Turn a page of comments on the page, or click "Load More"
# d. Find the request URL containing 'appmsgcomment', right-click → Copy → "Copy as cURL (bash)"
# e. Extract the -H 'Cookie: ...' section (the entire cookie string) and the URL from the curl command
# 2. Run the script
~/.claude/skills/wjs-publishing-wechat/scripts/fetch-comments-by-cookie.sh \
<article-folder> \
--url '<full URL, including the section with begin=0>' \
--cookie '<entire cookie string>'
Output:
<article-folder>/comments.md
(same format as fetch-comments.sh).
Caveats:
- The cookie in Path B needs to be re-captured every few hours — this is why Path A exists
- The field names / endpoints of the internal API may change with backend versions; the script uses heuristics to find common fields like / / . If the version changes, let Claude patch a few lines of JSON path on the spot
- Do not send the captured cookie to public channels like git or chat — it is equivalent to your login state
The final paragraph output to the user must be in this fixed format:
Ready. The article is in articles/YYYY-MM-DD-{slug}/
Publish (one command):
~/.claude/skills/wjs-publishing-wechat/scripts/upload-draft.sh \
articles/YYYY-MM-DD-{slug}
After success, go to mp.weixin.qq.com draft box to preview / publish.
article.md is the source file, use this for future revisions.
File Layout (Skill Itself)
~/.claude/skills/wjs-publishing-wechat/
├── SKILL.md # This file
├── README.md # Public readme (displayed on GitHub)
├── prompts/
│ ├── cover-prompt.md # AI cover image prompt template ([target words] placeholder)
│ └── illustration-prompt.md # AI explanatory illustration prompt template ([article content] placeholder)
└── scripts/
├── gen-cover-ai.sh # Cover image: 2.35:1 strict constraint, automatically cropped to 900×383
├── gen-illustration.sh # Explanatory illustration: Adaptive ratio, no cropping
├── upload-draft.sh # Step 6 main path: upload_image × 2 + create_draft + write publish.json + open browser
├── mass-send.sh # Step 7 (optional, enterprise subject only): mass/preview or mass/sendall + automatic comment/open
├── fetch-comments.sh # Step 7 (optional, enterprise subject only): Fetch all comments corresponding to msg_data_id → comments.md
├── fetch-comments-by-cookie.sh # Step 8 (optional, only available for personal subjects): Cookie fallback, fetch comments after browser packet capture
└── publish.sh # Legacy backup: Browser + clipboard manual flow (fallback when md2wechat is not configured)
Dependent external skills:
- (github.com/Wangnov/gpt-image-2-skill) — gen-cover-ai.sh / gen-illustration.sh call gpt-image-2 through this, only use (hard-coded in both scripts), requires . Does not support direct connection via OpenAI API key
- skill / CLI — upload-draft.sh uses its + commands (requires / , and current IP is in the whitelist)
Note:
(browser + clipboard manual publication flow) is still retained in the repository, only as a backup when md2wechat is not configured / cannot add IP whitelist. This skill no longer uses it in the default path.
Auto-publish: This skill is automatically synchronized to
github.com/jianshuo/claude-skills by
~/.claude/skills-publish-hook.sh
(automatically commit + push after each edit).
Polish Heuristics (Down to the Character)
Typo patterns → Correct:
- Misuse of "的/得/地": Judge according to grammar
- Repeated characters: "我我", "是是", "了了" → Delete one
- Homophones: Consider context ("在"vs"再", "做"vs"作")
Paragraph splitting时机:
- After finishing one idea, the next sentence changes subject → Split paragraph
- When "But", "However", "So", "Later" appear at the beginning of a sentence → Consider splitting paragraph
- A paragraph exceeds 80 words → Split at the nearest period
Do not split paragraphs:
- Parallel sentences, enumerations (maintain rhythm)
- Dialogue (follow dialogue format)
Anti-Patterns (Absolutely Not to Do)
| Do Not | Reason |
|---|
| Change "I think" to "The author believes" | Changes the author's identity |
| Add "subheadings" to interrupt the text | WeChat readers do not need navigation |
| Remove spoken sentence endings like "吧/呢/啊" | Removing them makes it no longer sound like the author |
| Add "Welcome to follow", "Like and share" at the end | The author will decide whether to add it |
| Change "today" to a specific date | The author intentionally uses "today" |
| Add examples / citations / data by yourself | This is writing, not completion |
| Output the full text directly without showing the diff | Users cannot see what you changed |
| Article exceeds 1500 words | Most likely an empty example is padding. Cut that paragraph and see if it naturally returns to 1000 words |
| Write a complete example to demonstrate a framework | Let take on the demonstration. Only keep the key points in the main body, do not repeat the structure diagram |
Showing the Diff
After each polishing, first tell the user what you changed, then ask if they want to continue:
I made 7 changes:
1. L3: "我我觉得" → "我觉得" (repeated character)
2. L8: Split long paragraph (120 words) into two paragraphs
3. L15: "通过…的方式" → "用…" (retain spoken style)
...
Do you want to see the complete result?
If there are ≤ 3 changes, you can directly provide the complete result without listing the diff.
Running the Skill (Practical Steps)
- Confirm the working directory (default , configurable by the user)
- Receive user input (paste or file)
- Write (user's original input)
- Write (polished version) → List the diff for the user
- Use AskUserQuestion to ask about title candidates
- Automatically run gen-cover-ai.sh to generate cover image + gen-illustration.sh to generate explanatory illustration (no need to ask the user)
- Generate ,
- Output publication guidelines
Done When