SuperAPI AI Directory

wjs-publishing-wechat

General & other

View repository

Updated 10-15 23:32

About this skill

wjs-publishing-wechat

Help the user write a WeChat Official Account article.Light polish, no rewriting. Automatically generate the cover image and explanatory images, and push the draft with one command.

⚠️ Sole style authority: STYLE.md (must-read before writing / polishing)

Before writing or polishing each article, first read the STYLE.mdin the same directory, and follow it strictly. That is the style definition file maintained by Wang Jianshuo himself—tone DNA, length, bolding, layout, and the red-line checklist are all there. This SKILL.md only coversmechanism and workflow(scripts, images, HTML conversion, publishing).

When STYLE.md and this file conflict on style,STYLE.md prevails. To adjust style = change STYLE.md, not here, and certainly not in the draft.

Below, only the mechanism-related writing constraints are kept:

  • Word count(STYLE.md sets the budget; here is the counting 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)))"
  • Mechanism for bold and red:upload-draft.sh Render **...** as red bold (usage see STYLE.md "bold and red").
  • Mechanism for Pangu spacing:upload-draft.sh Automatically run scripts/pangu.py, Claude does not need to add spaces manually.
  • How to write commands / code as standalone paragraphs(rules see STYLE.md; here is a copyable style):
    • Preferred—light background code block (raw HTML block, the whole paragraph on one line, no blank lines inside):
      <section style="background:#f6f8fa;border-radius:6px;padding:14px 16px;overflow-x:auto;font-family:Menlo,Consolas,monospace;font-size:14px;line-height:1.8;color:#24292e;">npm install -g xxx<br>xxx run</section>
      
    • Or fenced ```bash block, the script converts it into a standalone <p><code>…</code></p>。

For articles introducing a skill: the installation method must be attached at the end

Trigger condition: this article is introducing / recommending a specific Claude Code skill.

Prerequisite—confirm the skill has been published: Wang Jianshuo's own wjs-* skill is automatically pushed by ~/.claude/skills-publish-hook.sh to github.com/jianshuo/claude-skills, confirm with gh api repos/jianshuo/claude-skills/contents/<skill-name> . For other people's skills, first confirm they are in a public git repo.

Attach the following passage at the end(<SKILL_NAME> replace with the actual name):

## 安装方法

不用复制命令。打开你用的 AI agent——Claude Code、Codex、Kimi Code、OpenClaw 都可以,对它说一句:

> 安装 https://github.com/jianshuo/claude-skills/blob/main/<SKILL_NAME>/SKILL.md

它会自己 fetch、放到 skill 目录里、提示你重启对话。

用 Hermes 的话直接命令行:

\`\`\`bash
hermes skills install https://github.com/jianshuo/claude-skills/blob/main/<SKILL_NAME>/SKILL.md
\`\`\`

装完之后,对 agent 说一句「<一句最自然的触发语,紧扣这个 skill 的入口>」,就能用。

Rules:

  1. This passagedoes not count toward the 800–1000 word budget
  2. Use github.com/<owner>/<repo>/blob/main/<path>for URLs—browsers can view it directly, and LLM agents can also extract markdown from the blob URL
  3. List Hermes separatelycommand line, because it is a registry CLI rather than a chat agent
  4. Write the final trigger phrase according to the current skill's actual entry point,do not omit it
  5. Usually placed last; if there is ## 后注 then place it before the postscript

Workflow

Step 0: Receive input

Input forms: complete draft / scattered ideas / long passage without paragraph breaks / voice transcription (may contain typos). If too scattered,ask one question: "Do you want to write one article, or several independent ideas?"—ask only this once.

Step 1: Light polish

  • Fix typos ("的得地", homophones, repeated characters like "我我")
  • One paragraph every 1–3 sentences
  • Make minimal changes to awkward phrasing; if the tone changes after editing, better not to change it
  • Punctuation: full-width for Chinese, spaces between English / numbers
  • Keep the original opening and ending

Step 1.5: Privacy scan (hard gate before finalizing)

Trigger: after polishing, right before the text is finalized, run it on every article.Scan sentence by sentence for personal privacy information—specific names, restaurant / shop / venue names, precise locations / addresses, phone / email / WeChat ID, etc.—thesemust not go into the final published version, generalize, replace, or delete. For judgment boundaries, replacement examples, and details such as keeping city-level place names, see STYLE.md"10. Privacy red lines"。

Add the hits to the delivery change list (marked "Privacy"); if there are none at all, also say "Privacy scan: none." List anything uncertain and ask the user; do not delete intentional details on your own.

Step 2: Title candidates

Give 3 candidates: A) straightforward type; B) story type; C) the most flavorful sentence from the original. No clickbait, exaggeration, "shocking," or "must-read."

Step 3: Summary (50–80 characters)

Hook the reader, spark clicks—use a "curiosity gap": throw out the most counterintuitive / most tension-filled point in the article (conclusion, cost, turning point, number), buthold something back, keep the answer in the body, make people feel they have to click in. Still in Wang Jianshuo's tone (specific, sincere, restrained in the way it only tells half), the hook comes from something real, not adjectives;absolutely noclickbait, exaggeration, or marketing speak. Not a copy of the first paragraph. For rules and examples, see STYLE.md "6. Titles and summaries."

Step 4: Illustrations (two per article, generated automatically without asking the user)

  • Header image cover.png — strictly 2.35:1(900×383), strong typography, strong composition, text-led
  • Explanatory image illustration.png — aspect ratio determined by the content(model's choice), flat cartoon, with labels and flow
~/.claude/skills/wjs-publishing-wechat/scripts/gen-cover-ai.sh <article-folder> ["目标字词"]
~/.claude/skills/wjs-publishing-wechat/scripts/gen-illustration.sh <article-folder>
  • When no second parameter is passed, take from meta.json the title; it is recommended to pick core concept words (1–4 characters)
  • Internally goes through gpt-image-2-skill the --provider codex, requires ~/.codex/auth.json
  • The header image is automatically cropped to 900×383; the explanatory image is not cropped

The explanatory image must be referenced in markdown——article.md must have ![](./illustration.png) one line, otherwise it will not be visible in the draft.

⚠️ In the body, except for cover.png / illustration.png , images are not automatically uploaded to the CDN. For each local screenshot provided by the user (such as img-xxx.png), first md2wechat upload_image img-xxx.png --json get data.wechat_url, then replace article.md the local path in. Verification:grep -c mmbiz content.html = number of body images,grep -c 'img-' content.html = 0。

Default insertion position:after the final landing point of the body(if there is ## 后注 place before the postscript; if there is ## 安装方法 place before the installation method).

Never write a lead-in for the explanatory image—do not write things like "here is the whole thing drawn out" or "as shown in the figure"; let the image speak for itself. See [[no-illustration-caption]] for details.

Safety net:illustration.png exists but article.md is not referenced,upload-draft.sh automatically inserts and rewrites, idempotent.

Step 5: Output file package

In the working directory (default ~/wechat-publish/articles/) Create:

articles/2026-05-09-{slug}/
├── article.md           # 润色后的 markdown
├── cover.png            # 题图 900×383
├── illustration.png     # 解释图
├── meta.json            # { title, summary, author, date, slug }
└── original.md          # 用户原始输入

{slug}: Pinyin initials + keywords, within 30 characters.

Step 5.5: Choose title before publishing (hard gate, must wait for user reply)

Trigger: the file package is ready, right before running upload-draft.sh .This step cannot be skipped, and you cannot decide on your own—you must stop, send the title options to the user, and wait for the user to reply with a letter before continuing.

Give 4 candidates, formatted as a clear list:

  • A) Original title—i.e., the existing meta.json in title, listed as-is (so the user knows what "no change" looks like)
  • B / C / D) Three with more impact—more attention-grabbing and with more tension than the original title, but still observing Wang Jianshuo's red lines:no clickbait, no exaggeration, no "shocking / must-read / subversive", no marketing tone. Impact comes from real substance (counterintuitive conclusions, specific numbers, sharp contrasts, hooks that leave things half-said), not piles of adjectives.

After sending the candidates, say: "Reply A to use the original title, or B/C/D to choose a new one, or give one yourself." Thenstop。

After the user chooses:

  1. If B/C/D or self-authored → use python3 to change the meta.json in title to the selected one (cover.png has already been generated; there is no need to redraw the cover image because of a title change, unless the user requests it)
  2. Choose A or no reply → title unchanged
  3. Then continue to Step 6 publish

Step 6: Publish (upload-draft.sh)

~/.claude/skills/wjs-publishing-wechat/scripts/upload-draft.sh <workspace>/articles/YYYY-MM-DD-{slug}

What the script does:

  1. Run pangu.py to add Pangu spacing
  2. md2wechat upload_image cover.png → when thumb_media_id
  3. illustration.png exists but is not referenced, automatically insert it and upload to get the CDN URL (idempotent safety net)
  4. Inject the "Recent Articles" link list(build-recent-articles.py, on by default, see the next section)
  5. From article.md generate content.html(conversion rules below)
  6. Install draft.json, call create_draft or (publish.json when there is draft_media_id )draft/update update in place
  7. Automatically open the draft: on macOS use scripts/open-draft-edit.sh—read the session token of the logged-in browser, look up appmsgid、by title and go directly to the edit page for this article(if not logged in, fall back to opening the homepage); on Linux open mp.weixin.qq.com homepage.WECHAT_PUBLISH_NO_OPEN=1 turn off
  8. Automatically git add / commit / push the article directory to origin

End-of-article "Recent Articles" list (automatic)

On every publish,upload-draft.sh automatically appends at the end of the article arecently published articleslink list (5 by default), using the WeChat editor's native article hyperlink format (mp_article_text_link + data-linktype="2", only /s/ links from the same account are clickable).

Mechanism = local ledger + offline rendering, publishing step zero-network, deterministic, runnable offline:

  • Ledger: each article's publish.json in permalink(https://mp.weixin.qq.com/s/…)+ published_at。
  • Rendering:build-recent-articles.py <article-folder> scans articles in the same-level */publish.json that have permalink , takes the most recent N articles in reverse order of published_at (excluding this one), in article.md of <!--RECENT_ARTICLES_START/END--> within the marker areaIdempotent rewriteA raw-HTML block (content.html The builder passes it through as-is; WeChat ignores comments). If the ledger is empty, it simply won't attach anything—it never errors and never blocks publishing.
  • Run/preview separately:build-recent-articles.py <folder> --print(print only, don't write);--count N change the count.
  • Turn off:WECHAT_PUBLISH_NO_RECENT=1; change the count:WECHAT_RECENT_COUNT=8。

Where the ledger comes from — backfill-permalinks.py(run occasionally):permalink It can only be obtained after an article is officially published, from the WeChat Official Account backend (draft → publish is clicked manually and does not flow back). This script calls the web backend appmsgpublish(published list) to pull the entire account,matches exactly by titleand writes permalink/published_at back to each publish.json. Exact match only—published titles are often different from meta.title , and fuzzy matching would write the wrong links; anything that doesn't match will be listed for you to handle manually.

~/.claude/skills/wjs-publishing-wechat/scripts/backfill-permalinks.py [articles根目录] [--dry-run]

Authentication uses theweb session(not the publishing API's access_token); the cookie expires in a few hours, so this is "occasional maintenance" rather than something to run on every publish. Provide any one of three values: ① environment variable WECHAT_MP_COOKIE / WECHAT_MP_TOKEN / WECHAT_MP_FAKEID; ② session file (default ~/.config/wjs-wechat/mp-session.env, containing export with the same three variable names). How to capture: log in to mp.weixin.qq.com → Content Management → Published → in DevTools Network find the appmsgpublish request → Copy as cURL,token/fakeid is in the URL,Cookie is in the request headers.

upload-draft.sh When publishing, if the above credentials/session file are detected, it willfirst try its best to refresh the ledgerand then inject (non-fatal; if expired, skip); if not configured, it will just use the existing local ledger. Once the session file is configured, publishing becomes "automatic backfill + automatic attachment of recent articles" in one step.

article.md writing constraints(affects how Claude writes; other HTML details are handled by the script):

  • Supports <p> / <h2> / <h3> / <img> / <strong> / <em> / <code> / <ul> / <ol> / <li> / pipe table
  • Raw HTML block passthrough: blocks starting with < are output as-is; the entire section must be one block,no blank lines inside
  • Multiple lines within a paragraph → <br> line breaks(for parallelism / parallel short sentences):Hard rule: the end of a line must never be a comma ","; line-break boundaries may only fall after sentence-ending punctuation (。?!)

Environment variables:

  • WECHAT_PUBLISH_FORCE_NEW=1 — force creating a new draft (do not reuse draft_media_id)
  • WECHAT_PUBLISH_NO_OPEN=1 — do not automatically open the browser
  • WECHAT_PUBLISH_NO_PUSH=1 — do not automatically push
  • WECHAT_PUBLISH_NO_RECENT=1 — do not inject the "Recent Articles" list at the end
  • WECHAT_RECENT_COUNT=N — how many articles to list in "Recent Articles" (default 5)
  • WECHAT_MP_COOKIE / WECHAT_MP_TOKEN / WECHAT_MP_FAKEID or WECHAT_MP_SESSION(session file path, default ~/.config/wjs-wechat/mp-session.env) — if configured, automatically refresh the permalink ledger when publishing

Prerequisites:

  • md2wechat CLI installed and WECHAT_APPID + WECHAT_SECRET configured (md2wechat config show)
  • the current public IP is in the WeChat Official Account backend whitelist: mp.weixin.qq.com → Settings and Development → Basic Configuration → IP Whitelist. If missed, it will errcode=40164

Common errcodes:40164 IP not in whitelist | 45004 summary is empty / too short | 40007 old draft_media_id was deleted (the script automatically falls back to creating a new one).

After success, go to the Draft Box → mobile preview → publish.

Polishing guidance / paragraphing / red lines / change list

All moved to STYLE.md—typo patterns, paragraphing rules, red line checklist, and providing a change list before delivery are all there. Read STYLE.md before polishing and follow it.

Depends on external skills:gpt-image-2-skill(cover/illustration generation) + /md2wechat(upload + draft)。

Completion criteria

  • articles/YYYY-MM-DD-{slug}/ folder exists
  • contains article.md, cover.png, illustration.png, meta.json, original.md
  • meta.json has all fields
  • privacy scan done before finalizing(Step 1.5)—personal privacy information such as names / restaurants / locations has been generalized or removed, and hits are included in the change list
  • Before publishing, A (original title) + 3 more impactful candidates were provided, and the title was finalized according to the user's choice(Step 5.5)
  • draft is visible in the mp.weixin.qq.com backend
  • the user did not say "revise again"