Skip to main content
ClaudeWave
Skill107 repo starsupdated 4d ago

wjs-publishing-wechat

This Claude Code skill assists users in writing and polishing WeChat Official Account articles. It handles light editing rather than complete rewrites, automatically generates cover images and explanatory graphics, and prepares drafts for upload to mp.weixin.qq.com. Use it when you have scattered ideas, rough drafts, or notes for a WeChat article and need professional refinement, visual assets, and publication-ready formatting.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/jianshuo/claude-skills /tmp/wjs-publishing-wechat && cp -r /tmp/wjs-publishing-wechat/wjs-publishing-wechat ~/.claude/skills/wjs-publishing-wechat
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# wjs-publishing-wechat

帮用户写微信公众号文章。**轻润色,不重写。** 自动生成题图和解释图,一行命令推草稿。

## ⚠️ 风格唯一权威:STYLE.md(写作 / 润色前必读)

**写或润色每一篇文章前,先读同目录的 [`STYLE.md`](./STYLE.md),并严格遵循。** 那是王建硕本人维护的风格定义文件——语气 DNA、长度、加粗、排版、红线清单全在那里。本 SKILL.md 只管**机制与工作流**(脚本、配图、HTML 转换、发布)。

风格上 STYLE.md 与本文件冲突时,**以 STYLE.md 为准**。要调风格 = 改 STYLE.md,不在这里改、更不在草稿里改。

下面只保留机制相关的写作约束:

- **字数**(STYLE.md 定预算,这里给计数命令):

```bash
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)))"
```

- **加粗加红的机制**:`upload-draft.sh` 把 `**...**` 渲染成红色粗体(用法见 STYLE.md「加粗加红」)。
- **盘古之白的机制**:`upload-draft.sh` 自动跑 `scripts/pangu.py`,Claude 不用手动加空格。
- **命令 / 代码独立成段的写法**(规则见 STYLE.md,这里给可复制的样式):
  - **首选**——淡底色代码块(raw HTML 块,整段一行,内部不能有空行):
    ```
    <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>
    ```
  - 或 fenced ```` ```bash ```` 块,脚本转成独立 `<p><code>…</code></p>`。

## 介绍 skill 的文章:末尾必须附安装方法

**触发条件**:这篇在介绍 / 推荐某个具体的 Claude Code skill。

**前置 — 确认 skill 已发布**:王建硕自己的 `wjs-*` skill 由 `~/.claude/skills-publish-hook.sh` 自动 push 到 [github.com/jianshuo/claude-skills](https://github.com/jianshuo/claude-skills),用 `gh api repos/jianshuo/claude-skills/contents/<skill-name>` 确认。别人的 skill 先确认在公开 git repo 里。

**末尾附下面这段**(`<SKILL_NAME>` 替换成实际名):

```markdown
## 安装方法

不用复制命令。打开你用的 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 的入口>」,就能用。
```

规则:
1. 这段**不计入** 800–1000 字预算
2. URL 用 `github.com/<owner>/<repo>/blob/main/<path>`——浏览器能直接看,LLM agent 也能从 blob URL 抽 markdown
3. Hermes 单独列**命令行**,因为它是 registry CLI 而非 chat agent
4. 最后那句触发语按当前 skill 实际入口写,**不要漏**
5. 通常放最后;有 `## 后注` 则放后注之前

## 工作流

### Step 0: 接收输入

输入形式:完整草稿 / 散乱思路 / 长段没分段 / 语音转写(可能有错字)。太散就**问一个问题**:"想写一篇文章,还是几个独立想法?"——只问这一次。

### Step 1: 轻润色

- 修错字("的得地"、同音字、"我我"重复字)
- 每 1–3 句一段
- 拗口处做最小改动;改完语气变了宁可不改
- 标点:中文全角,英文 / 数字间空格
- 保留原本开头和结尾

### Step 1.5: 隐私扫描(定稿前硬关卡)

**触发**:润色完、文字即将定稿之前,每篇都过。**逐句扫个人隐私信息**——具体人名、餐厅 / 店铺 / 场所名、精确地点 / 住址、电话 / 邮箱 / 微信号等——这些**不能进最终发布版**,泛化替换或删除。判定边界、替换示例、城市级地名照留等细则见 [`STYLE.md`「10. 隐私红线」](./STYLE.md)。

命中的项并进交付改动清单(标「隐私」);一处都没有也说一句「隐私扫描:无」。拿不准的列出来问用户,不擅自删有意为之的细节。

### Step 2: 标题候选

给 **3 个候选**:A) 直白型;B) 故事型;C) 原文里最有味道的一句。不做标题党、夸张、"震惊"、"必看"。

### Step 3: 摘要(50–80 字)

**勾住读者、激发点击**——用「好奇缺口」:抛出文章里最反直觉 / 最有张力的那个点(结论、代价、转折、数字),但**留一手**,把答案留在正文,让人非点进来不可。仍是王建硕语气(具体、诚恳、话说一半的克制),钩子来自真东西不是形容词;**绝不**标题党、夸张、营销腔。不是第一段的复制。规则与例子见 STYLE.md「6. 标题与摘要」。

### Step 4: 配图(每篇两张,自动生成不问用户)

- **题图 cover.png** — **严格 2.35:1**(900×383),强字体、强构图、文字主导
- **解释图 illustration.png** — **比例由内容决定**(模型自选),扁平卡通、有标签和流程

```bash
~/.claude/skills/wjs-publishing-wechat/scripts/gen-cover-ai.sh <article-folder> ["目标字词"]
~/.claude/skills/wjs-publishing-wechat/scripts/gen-illustration.sh <article-folder>
```

- 不传第二参数时从 `meta.json` 取 `title`;建议挑核心概念字词(1–4 字)
- 内部走 `gpt-image-2-skill` 的 `--provider codex`,需要 `~/.codex/auth.json`
- 题图自动裁到 900×383;解释图不裁

**解释图必须在 markdown 里被引用**——`article.md` 要有 `![](./illustration.png)` 一行,否则草稿里看不到。

**⚠️ 正文里除 `cover.png` / `illustration.png` 外的图不会自动上 CDN。** 用户给的本地截图(如 `img-xxx.png`)每张先 `md2wechat upload_image img-xxx.png --json` 拿 `data.wechat_url`,再替换 `article.md` 里的本地路径。验证:`grep -c mmbiz content.html` = 正文图片数,`grep -c 'img-' content.html` = 0。

默认插入位置:**正文最后落点之后**(有 `## 后注` 放后注前;有 `## 安装方法` 放安装方法前)。

**绝不给解释图写引导语**——不写「整件事画起来是这样」「如图所示」之类,图自己说话。详见 [[no-illustration-caption]]。

> 安全网:`illustration.png` 存在但 `article.md` 没引用时,`upload-draft.sh` 自动插入并改写,幂等。

### Step 5: 输出文件包

在工作目录(默认 `~/wechat-publish/articles/`)创建:

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

`{slug}`:拼音首字母 + 关键词,30 字符内。

### Step 5.5: 发布前选标题(硬关卡,必须等用户回复)

**触发**:文件包就绪、即将跑 `upload-draft.sh` 之前。**这一步不可跳过、不能自作主张选**——必须停下来,把标题选项发给用户、等用户回一个字母再继续。

给 **4 个候选**,排版成一目了然的列表:

- **A) 原标题**——即 `meta.json` 里现有的 `title`,原样列出(让用户知道「不改」长什么样)
- **B / C / D) 更有冲击力的三个**——比原标题更抓人、张力更足,但仍守王建硕红线:**不标题党、不夸张、不「震惊 / 必看 / 颠覆」、不营销腔**。冲击力来自真东西(反直觉的结论、具体的数字、尖锐的对比、话说一半的钩子),不是形容词堆砌。

发完候选后说一句:「回 A 用原标题,或 B/C/D 选新的,也可以自己给一个。」然后**停**。

用户选定后:
1. 若选 B/C/D 或自拟 → 用 `python3` 把 `meta.json` 的 `title` 改成选中的那条(`cover.png` 已生成,不必因标题变化重画题图,除非用户要求)
2. 选 A 或没回 → 标题不动
3. 再继续 Step 6 发布

### Step 6: 发布(`upload-draft.sh`)

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

脚本做的事:

1. 跑 `pangu.py` 加盘古之白
2. `md2wechat upload_image cover.png` → 拿 `thumb_media_id`
3. `illustration.png` 存在但没引用时自动插入并 upload 拿 CDN URL(幂等安全网)
4. **注入「最近文章」链接列表**(`build-recent-articles.py`,默认开,见下节)
5. 从 `article.md` 生成 `content.html`(转换规则见下)
6. 装 `draft.json`,调 `create_draft` 或(`publish.json` 有 `draft_media_id` 时)`draft/update` 原地更新
7. 自动打开草稿:macOS 走 `scripts/open-draft-edit.sh`——读已登录浏览器的 session token、按标题查出 `appmsgid`、**直达这一篇的编辑页**(没登录则回退开首页);Linux 开 `mp.weixin.qq.com` 首页。`WECHAT_PUBLISH_NO_OPEN=1` 关掉
8. 自动 `git add / commit / push` 文章目录到 origin

### 文末「最近文章」列表(自动)

每次发布,`upload-draft.sh` 会在文末自动挂一个**最近已发布文章**的链接列表(默认 5 篇),用微信编辑器原生的文章超链接格式(`mp_article_text_link` + `data-linktype="2"`,同账号 `/s/` 链接才点得动)。

机制 = **本地账本 + 离线渲染**,发布步骤零网络、确定性、离线可跑:

- **账本**:每篇 `publish.json` 里的 `permalink`(`https://mp.weixin.qq.com/s/…`)+ `published_at`。
- **渲染**:`build-recent-articles.py <article-folder>` 扫同级 `*/publish.json` 里有 `permalink` 的文章,按 `published_at` 倒序取最近 N 篇(排除本篇),在 `article.md` 的 `<!--RECENT_ARTICLES_START/END-->` 标记区内**幂等重写**一个 raw-HTML 块(`content.html` 构建器原样透传,微信忽略注释)。账本为空就不挂,从不报错、从不阻断发布。
- 单独跑/预览:`bui
skill-quality-reviewerSubagent

Repo-wide drift detector for the wjs-* Claude Code skills in this marketplace. Sweeps every SKILL.md, scores it against the repo's own conventions (V-ing naming, trigger-phrase density, companion files, description shape), and returns a grouped punch list ordered by severity. Read-only — never edits files. Use before pushing a batch of skill changes, or whenever you wonder "are these skills still internally consistent?

wangjianshuo-perspectiveSkill

|

wjs-auditing-projectSkill

Use when the user asks to audit what's wrong with a project, "make it right", "看看项目出了什么问题", "为什么用户的需求还没上线", "为什么没提交App Store", "为什么没新build", or wants a holistic state-of-the-project check covering unmerged branches, stalled PRs, failed GitHub Actions, stale builds, plan drift (TODOS.md / ROADMAP), unreleased commits, and log errors. Runs read-only investigation, presents a grouped checklist, fixes only after explicit user confirmation. Aware of the Cathier iOS app workflow (Xcode + fastlane + auto-merge @claude PRs from in-app feedback).

wjs-burning-subtitlesSkill

Use when the user has a video + an SRT and wants the subtitles either burned into the pixels (libass, always-visible) or soft-muxed as a togglable track. Also handles the final composite step for the localization pipeline — burn subs, mix a dub track, and keep the original audio as a low-volume bed, all in ONE ffmpeg encode (no cascade). Verifies libass availability and auto-downloads a static evermeet ffmpeg build when Homebrew's stripped binary lacks it. Triggers — "烧字幕", "硬字幕", "burn subtitles", "burn-in subs", "embed subtitle", "soft mux SRT", "把字幕烧进视频", "做最终合成".

wjs-cleaning-spamSkill

Use when the user complains about spam on his X/Twitter posts — 同城面付 / 寻固炮 / 线下上门 / 免费破处 这类引流号在他推文下刷的 emoji 垃圾回复 — and wants them removed. Covers the last 7 days (X recent-search window). Triggers — "把这些spam删掉", "清理X垃圾回复", "推文下面好多引流号", "clean spam replies", "/wjs-cleaning-spam".

wjs-converting-text-to-videoSkill

Use when the user wants a 王建硕-style WeChat article (article.md) turned into a narrated short MP4 video — TTS voiceover via 火山引擎 Volcano TTS, HyperFrames CSS/GSAP animation per scene, subtle SFX, abstract watercolor background, full pipeline rendering to 1080×1920 portrait MP4 (30-90s). Triggers — "把这篇文章做成视频", "做一个解说视频", "讲解视频", "/wjs-converting-text-to-video".

wjs-converting-wp-to-hugoSkill

Use when migrating a WordPress site to a Hugo static site on GitHub Pages from a WXR export (.xml) plus the wp-content/uploads folder — preserving /archives/<id>/ URLs, localizing images, and deploying via GitHub Actions. Triggers — "把 WordPress 迁成 Hugo", "wordpress 转静态站", "migrate WordPress to Hugo", "WXR to Hugo", "publish WordPress to GitHub Pages", "/wjs-converting-wp-to-hugo".

wjs-dubbing-videoSkill

Use when the user has a video + a target-language SRT and wants the video to actually speak that language — generates a time-aligned TTS voice dub. Routes by voice ID — Volcano (豆包) TTS for Chinese, edge-tts neural for any language. Defaults to one voice (single-speaker); opt-in multi-speaker via visual diarization. Outputs `*_<lang>_dub.mp4` with the dub audio in place of the original. Final mixing (audio bed + burn-in) is handed off to `/wjs-burning-subtitles`. Triggers — "配音", "中文配音", "Chinese dub", "voice over this", "dub the video", "TTS this SRT", "different voice for each speaker".