wjs-tweeting-from-articles

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

wjs-tweeting-from-articles

wjs-tweeting-from-articles

每天一条 X tweet,灵感直接从最近发的公众号文章里抠。文章是源头,tweet 是更短的萃取。
One daily X tweet, with inspiration directly extracted from recently published WeChat Official Account articles. The article is the source, and the tweet is a more concise distillation.

Core Principle

Core Principle

文章已经过了一轮王建硕式的精炼,tweet 只是再短一格的版本。 不要重新构思——从 article.md 里直接抠最 quotable 的一句 / 一段,按 X 的节奏切。
一天最多一条,一篇文章只推一次。 状态文件
state/history.jsonl
记录哪些 article 已经推过;不重复。
真发,不暂存。 用户确认后立刻
xurl POST
。失败原文 dump 给用户重试。
The article has already gone through Wang Jianshuo-style refinement; the tweet is just an even shorter version. Do not re-conceptualize — directly extract the most quotable sentence/paragraph from article.md and adapt it to X's rhythm.
Maximum one tweet per day, one tweet per article. The state file
state/history.jsonl
records which articles have been tweeted; no repetition.
Post immediately, no draft. After user confirmation, send via
xurl POST
immediately. If it fails, dump the original content to the user for retry.

When This Skill Fires

When This Skill Fires

  • 用户说「今天的 tweet」/「发一条 X」/「从文章里发推」
  • 用户跑
    /wjs-tweeting-from-articles
  • 用户设了
    /schedule daily /wjs-tweeting-from-articles
    之后每天自动调
  • User says "Today's tweet" / "Post an X tweet" / "Tweet from an article"
  • User runs
    /wjs-tweeting-from-articles
  • User sets up
    /schedule daily /wjs-tweeting-from-articles
    for automatic daily execution

When NOT to use

When NOT to use

  • 用户要发的内容和最近的公众号文章无关——直接
    xurl POST /2/tweets
    即可
  • 用户要发的是产品 / skill 推广——用
    /publish-skill
    /wjs-promoting-skills
  • The content the user wants to post is unrelated to recent WeChat Official Account articles — directly use
    xurl POST /2/tweets
  • The user wants to promote a product/skill — use
    /publish-skill
    or
    /wjs-promoting-skills

Workflow

Workflow

Step 1: 挑今天要推的文章

Step 1: Select today's article to tweet

bash
scripts/pick-next-article.sh
输出最新一篇还没推过的文章的 folder 路径(最近一周内,按日期倒序找第一个未推的)。已经全推完 → 退出 0、空输出,告诉用户「最近 7 天的文章都推过了,今天 rest day」。
如果用户想推特定那一篇:跳过此脚本,直接用
<folder>
bash
scripts/pick-next-article.sh
Outputs the folder path of the latest article that hasn't been tweeted yet (within the last week, find the first untweeted one in reverse chronological order). If all have been tweeted → exit with code 0, empty output, and inform the user "All articles from the last 7 days have been tweeted, today is a rest day".
If the user wants to tweet a specific article: skip this script and directly use
<folder>
.

Step 2: 读文章 + 草拟 3 条 tweet 候选

Step 2: Read article + draft 3 tweet candidates

<folder>/article.md
,按下面三个角度各起草一条:
角度选材例子
A · 金句从文中挑一句最 quotable 的,可以加一句铺垫"笔头钝了,写不出锋——蘸点墨,在砚台上转两圈,重新有了尖。"
B · 比喻文章的核心比喻 + 一句把比喻落到读者头上"写 prompt 跟画画一样,是手感活儿。每天都得写点。"
C · 反差「不是 X,是 Y」式的认知翻转"不是变聪明,是手感来了。"
长度硬约束:tweet ≤ 280 字符(X 限制;中文每字算 2)——所以中文 tweet 实际 ≤ 140 字留 buffer 到 120 字以内比较稳。
风格:保留王建硕语气——平实、家常比喻、不写营销腔。不要加 hashtags、不要加 @、不要加 emoji(除非原文有)。不要加 mp.weixin 链接(默认);如果用户问要不要带链接,提示可以 reply 里附。
Read
<folder>/article.md
, draft one candidate from each of the following three angles:
AngleContent SelectionExample
A · QuotePick the most quotable sentence from the article, can add a lead-in"When your pen tip goes blunt and can't write sharply — dip it in ink, twist it twice on the inkstone, and it regains its sharpness."
B · MetaphorThe core metaphor of the article + a sentence that connects it to the reader"Writing prompts is like painting — it's a matter of muscle memory. You have to write a little every day."
C · ContrastCognitive flip in the form of "It's not X, it's Y""It's not about getting smarter, it's about getting into the groove."
Hard length constraint: Tweet ≤ 280 characters (X's limit; each Chinese character counts as 2) — so actual Chinese tweets ≤ 140 characters. Keeping it within 120 characters is safer for buffer.
Style: Retain Wang Jianshuo's tone — plain, down-to-earth metaphors, no marketing jargon. Do not add hashtags, @ mentions, or emojis (unless present in the original text). Do not add mp.weixin links (by default); if the user asks about adding links, suggest attaching them in a reply.

Step 3: 让用户挑一条

Step 3: Let the user select one

AskUserQuestion
给出 A/B/C 三条候选 + 「四选其他」。用户选一条之后进入 Step 4。
Use
AskUserQuestion
to present the three A/B/C candidates plus a "Fourth option: other". After the user selects one, proceed to Step 4.

Step 4: 真发 X

Step 4: Post to X for real

bash
TWEET_TEXT='<picked text>'
JSON=$(jq -nc --arg text "$TWEET_TEXT" '{text:$text}')
resp=$(xurl -X POST -d "$JSON" /2/tweets)
bash
TWEET_TEXT='<picked text>'
JSON=$(jq -nc --arg text "$TWEET_TEXT" '{text:$text}')
resp=$(xurl -X POST -d "$JSON" /2/tweets)

Don't use
jq -r '.data.id'
here — X API returns raw newlines in the echoed

Don't use
jq -r '.data.id'
here — X API returns raw newlines in the echoed

text
field, which strict jq rejects with "control characters must be escaped".

text
field, which strict jq rejects with "control characters must be escaped".

Grep the id directly instead.

Grep the id directly instead.

TWEET_ID=$(printf '%s' "$resp" | grep -oE '"id":"[0-9]+"' | head -1 | sed -E 's/."([0-9]+)"./\1/') [[ -n "$TWEET_ID" ]] || { echo "POST failed: $resp"; exit 1; } echo "https://x.com/jianshuo/status/$TWEET_ID"

成功 → 拿到 tweet_id + URL,告诉用户。
TWEET_ID=$(printf '%s' "$resp" | grep -oE '"id":"[0-9]+"' | head -1 | sed -E 's/."([0-9]+)"./\1/') [[ -n "$TWEET_ID" ]] || { echo "POST failed: $resp"; exit 1; } echo "https://x.com/jianshuo/status/$TWEET_ID"

If successful → get the tweet_id + URL and inform the user.

Step 5: 记录到 history

Step 5: Record to history

bash
HIST="$HOME/.claude/skills/wjs-tweeting-from-articles/state/history.jsonl"
SLUG=$(basename "$FOLDER")
jq -nc --arg date "$(date +%F)" --arg slug "$SLUG" --arg angle "$ANGLE" \
       --arg tweet_id "$TWEET_ID" --arg text "$TWEET_TEXT" \
       '{date:$date,slug:$slug,angle:$angle,tweet_id:$tweet_id,text:$text,status:"posted"}' \
  >> "$HIST"
angle
A
/
B
/
C
/
other
bash
HIST="$HOME/.claude/skills/wjs-tweeting-from-articles/state/history.jsonl"
SLUG=$(basename "$FOLDER")
jq -nc --arg date "$(date +%F)" --arg slug "$SLUG" --arg angle "$ANGLE" \
       --arg tweet_id "$TWEET_ID" --arg text "$TWEET_TEXT" \
       '{date:$date,slug:$slug,angle:$angle,tweet_id:$tweet_id,text:$text,status:"posted"}' \
  >> "$HIST"
angle
A
/
B
/
C
/
other
.

Step 6: 收尾

Step 6: Wrap up

告诉用户:
  • tweet URL
  • 哪篇文章(slug)
  • 哪个 angle
  • 今天的 history 行已经写入
Inform the user:
  • Tweet URL
  • Which article (slug)
  • Which angle was used
  • Today's history entry has been written

Inputs

Inputs

/wjs-tweeting-from-articles                       # 自动挑最近一篇没推过的
/wjs-tweeting-from-articles <article-folder>      # 显式指定
/wjs-tweeting-from-articles --dry-run             # 草稿不发
/wjs-tweeting-from-articles                       # Automatically select the latest untweeted article
/wjs-tweeting-from-articles <article-folder>      # Explicitly specify an article
/wjs-tweeting-from-articles --dry-run             # Draft only, no posting

File Layout

File Layout

~/.claude/skills/wjs-tweeting-from-articles/
├── SKILL.md
├── scripts/
│   └── pick-next-article.sh    # 找最近一篇未推的 article folder
└── state/
    ├── .gitignore              # 屏蔽 history.jsonl 不被推到 public repo
    └── history.jsonl           # 每条 tweet 一行 JSON record
~/.claude/skills/wjs-tweeting-from-articles/
├── SKILL.md
├── scripts/
│   └── pick-next-article.sh    # Find the latest untweeted article folder
└── state/
    ├── .gitignore              # Exclude history.jsonl from public repo
    └── history.jsonl           # One JSON record per tweet, line-separated

Daily 自动化(可选)

Daily Automation (Optional)

要每天自动跑:
bash
/schedule daily 09:00 /wjs-tweeting-from-articles
或写 cron。但默认不自动跑——每天人工确认 angle 选哪条更稳。
To run automatically every day:
bash
/schedule daily 09:00 /wjs-tweeting-from-articles
Or use cron. But by default, do not run automatically — manually confirming which angle to choose each day is more reliable.

Anti-Patterns

Anti-Patterns

不要原因
加 hashtags(#AI #prompt)王建硕的 X 风格不用 hashtag,加了变营销腔
同一篇文章推两条一篇一推;如果文章特别长 / 多核心,下次跑时把它从 history 删一行重推
tweet 里塞 mp.weixin 链接默认不带;想带就放 reply 里
把 3 条候选都发出去用户挑 1 条;不挑 = 跳过今天
凭空生造一条不在文章里的 tweet灵感必须从 article.md 抠;这条 skill 的价值就是「文章是源」
LLM 自己改原文风格做"提炼"王建硕原文已经够紧;直接抠 > 重写
Do NotReason
Add hashtags (#AI #prompt)Wang Jianshuo's X style doesn't use hashtags; adding them makes it sound like marketing
Tweet twice from the same articleOne tweet per article; if the article is particularly long/multi-core, delete one entry from history and retweet next time
Include mp.weixin links in the tweetDo not include by default; if you want to add it, put it in a reply
Post all 3 candidatesThe user selects 1; if no selection is made, skip today
Create a tweet from scratch that's not in the articleInspiration must be extracted from article.md; the value of this skill is that "the article is the source"
Let LLM rewrite the original text in its own style for "refinement"Wang Jianshuo's original text is already concise enough; direct extraction is better than rewriting

Dependencies

Dependencies

  • xurl
    xurl whoami
    能返回用户名(auth OK)
  • jq:解析 xurl 返回的 JSON
  • 存在
    ~/code/wechat-publish/articles/YYYY-MM-DD-*/article.md
    :源文章
  • xurl:
    xurl whoami
    can return the username (authentication OK)
  • jq: Parse JSON returned by xurl
  • Presence of
    ~/code/wechat-publish/articles/YYYY-MM-DD-*/article.md
    : Source articles