wjs-tweeting-from-articles について
最近の WeChat 記事をもとに X の投稿を作成・公開するスキルです。ユーザーが選ぶ毎日1件の投稿フローに加え、複数記事をキューに入れて間隔を空けて公開する方法もあります。
主な機能
- 01最近の未投稿記事を選択
- 02記事から3つの投稿案を作成
- 03選択後に投稿を公開
- 04投稿済み記事を履歴で記録
- 05間隔を設けて投稿をキュー配信
活用シーン
- 01記事をもとに毎日1件投稿
- 02複数記事からの投稿を予約
- 03使用済み記事の再投稿を防止
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jianshuo/ claude-skills --skill wjs-tweeting-from-articlesインストール後、Claude Code で/wjs-tweeting-from-articlesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
com.jianshuo.wjs-tweeting-from-articles.plist.templatedaily.shsetup.shuninstall.shscripts/mirror-articles.pypick-next-article.shpick-next-article.sh.bak.1779689674state/.gitignorequeue-2026-05-24.tsv詳細
wjs-tweeting-from-articles
毎日 X に 1 件投稿します。インスピレーションは最近公開した公式アカウントの記事から直接拾います。記事が元で、tweet はそれをさらに短く抽出したものです。
Core Principle
記事はすでに王建硕風に一度磨かれており、tweet はそれをもう一段短くしたものにすぎません。 新たに考え直さず、article.md から最も引用したくなる一文/一段落をそのまま抜き出し、X のリズムに合わせて区切ります。
1 日に最大 1 件、1 つの記事につき投稿は 1 回だけです。 状態ファイル state/history.jsonl に、投稿済みの記事を記録します。重複投稿はしません。(例外:一括スケジュールモード——複数の記事を一度にキューに登録し、N 時間ごとに 1 件ずつ自動投稿します。詳しくは以下を参照。)
下書き保存ではなく、実際に投稿します。 ユーザーの確認後、すぐに xurl POST を実行します。失敗した場合は元の内容をそのまま出力し、ユーザーが再試行できるようにします。
When This Skill Fires
- ユーザーが「今日の tweet」「X に投稿して」「記事からツイートして」と言ったとき
- ユーザーが
/wjs-tweeting-from-articlesを実行したとき - ユーザーが
/schedule daily /wjs-tweeting-from-articlesを設定し、その後毎日自動で呼び出されたとき
When NOT to use
- ユーザーが投稿したい内容が最近の公式アカウントの記事と関係ない場合——そのまま
xurl POST /2/tweetsを実行してください - ユーザーが投稿したいのが製品/スキルの宣伝の場合——
/publish-skillまたは/wjs-promoting-skillsを使ってください
Workflow
Step 1: 今日投稿する記事を選ぶ
scripts/pick-next-article.sh
投稿済みでない最新の記事のフォルダーパスを出力します(直近 1 週間以内から、日付の降順で最初の未投稿記事を探します)。すべて投稿済みの場合は、終了コード 0 で出力なしとし、「直近 7 日間の記事はすべて投稿済みです。今日は rest day にしましょう」とユーザーに伝えます。
ユーザーが特定の記事を投稿したい場合は、このスクリプトをスキップし、直接 <folder> を使います。
Step 2: 記事を読み、tweet 候補を 3 件作成する
<folder>/article.md を読み、以下の3 つの観点それぞれで 1 件ずつ草案を作成します。
| 観点 | 選び方 | 例 |
|---|---|---|
| A · 名言 | 本文から最も引用したくなる一文を選び、前置きを 1 文加えてもかまいません | "筆先が鈍ると、鋭い言葉が書けなくなる。墨をつけて、硯の上で二回ほど回せば、また尖ってくる。" |
| B · 比喩 | 記事の中心となる比喩と、その比喩を読者自身に引き寄せる一文 | "prompt を書くのは絵を描くのと同じで、手の感覚がものを言います。毎日少しは書きましょう。" |
| C · 対比 | 「X ではなく、Y」という認識の転換 | "賢くなったんじゃない。手の感覚が戻ってきたんです。" |
文字数の厳守事項:tweet は 280 文字以下(X の制限。中国語は 1 文字につき 2 文字として計算)——そのため、中国語の tweet は実質 140 文字以下です。120 文字以内に収めると余裕があって安心です。
スタイル:王建硕の語り口を保ちます——飾らず、日常的な比喩を使い、宣伝っぽい表現は避けます。ハッシュタグや @、絵文字は追加しないでください(原文にある場合を除きます)。mp.weixin のリンクも追加しないでください(デフォルト)。ユーザーからリンクを付けるべきか尋ねられた場合は、reply に添付できると伝えます。
Step 3: ユーザーに 1 件選んでもらう
AskUserQuestion を使って A/B/C の 3 件の候補と「その他(4 つ目の選択肢)」を提示します。ユーザーが 1 件選んだら Step 4 に進みます。
Step 4: X に実際に投稿する
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
# `text` field, which strict jq rejects with "control characters must be escaped".
# 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 を取得してユーザーに伝えます。
Step 5: history に記録する
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: 完了を伝える
ユーザーに以下を伝えます。
- tweet の URL
- 記事(slug)
- angle
- 今日の history 行を記録済みであること
Inputs
/wjs-tweeting-from-articles # 自动挑最近一篇没推过的
/wjs-tweeting-from-articles <article-folder> # 显式指定
/wjs-tweeting-from-articles --dry-run # 草稿不发
File Layout
~/.claude/skills/wjs-tweeting-from-articles/
├── SKILL.md
├── scripts/
│ ├── pick-next-article.sh # 找最近一篇未推的 article folder
│ └── post-next-from-queue.sh # 批量排期模式:每次发队列里下一条,自节流
└── state/
├── .gitignore # 屏蔽 history.jsonl 不被推到 public repo
├── history.jsonl # 每条 tweet 一行 JSON record
├── queue-<DATE>.tsv # 批量模式:待发队列 idx/slug/text
├── queue-cursor # 批量模式:下一条序号
└── last-post-epoch # 批量模式:上次发出的时间戳(节流用)
一括スケジュールモード(複数の記事を一度に予約し、N 時間ごとに 1 件投稿)
複数の記事を一度に投稿したい場合(よくある例:wjs-mining-articles で長時間の対談から十数本の記事を掘り起こした場合)は、このモードを使います。一度に連続投稿すると X にスパムと判定されるため、キューに登録し、一定の間隔で自動投稿します。
記事ごとに angle を AskUserQuestion で選ばない理由:十数本の記事それぞれに A/B/C を用意すると多すぎます。各記事から最も引用したくなる一文を1 件だけ直接抜き出し(120 文字以下、王建硕の語り口、ハッシュタグ/絵文字/リンクなし、句読点の前後に半角スペースを入れる)、候補をすべてユーザーに一度に見せて、投稿の間隔を決めてもらいます。確認後、スケジュールを設定します。
仕組み(scripts/post-next-from-queue.sh と毎時の cron。スクリプト自体が投稿間隔を制御します):
# 1. 队列文件:state/queue-<DATE>.tsv,每行 idx<TAB>slug<TAB>text
# 2. cursor: state/queue-cursor(下一条序号);last-post-epoch(上次发的时间)
# 3. 立刻发第一条(FORCE 绕过节流,顺便验证 xurl 能发):
FORCE=1 bash scripts/post-next-from-queue.sh
# 4. 装 cron:每小时跑,脚本只在距上次 ≥ MIN_GAP(默认 4h)才真发:
( crontab -l 2>/dev/null | grep -v post-next-from-queue.sh; \
echo "5 * * * * /bin/bash $HOME/.claude/skills/wjs-tweeting-from-articles/scripts/post-next-from-queue.sh >/dev/null 2>&1" ) | crontab -
スクリプトのポイント:単一マシン上のロック(同時実行を防止)、投稿に失敗した場合は cursor を進めない(次の 1 時間後に再試行)、投稿に成功した場合のみ history.jsonl に記録して cursor を進める、キューの投稿が完了したら、自身の cron 行を自動削除します。間隔を変更するには MIN_GAP(秒)を使います。
Daily 自動化(任意)
毎日自動で実行するには、以下を設定します。
/schedule daily 09:00 /wjs-tweeting-from-articles
または cron に設定します。ただし、デフォルトでは自動実行しません——毎日ユーザーが angle を確認して選ぶほうが確実です。
Anti-Patterns
| しないこと | 理由 |
|---|---|
| ハッシュタグ(#AI #prompt)を追加する | 王建硕の X ではハッシュタグを使いません。追加すると宣伝っぽくなります |
| 同じ記事から 2 件投稿する | 1 つの記事につき 1 件です。記事が特に長い/中心となるテーマが複数ある場合は、次回の実行時に history から該当する 1 行を削除して再投稿してください |
| tweet に mp.weixin のリンクを含める | デフォルトでは含めません。付けたい場合は reply に記載してください |
| 3 件の候補をすべて投稿する | ユーザーが 1 件選びます。選ばれなかった場合は、その日の投稿をスキップします |
| 記事にない tweet をでっち上げる | インスピレーションは必ず article.md から拾います。このスキルの価値は「記事が元」であることです |
| LLM が原文のスタイルを勝手に変えて「要約」する | 王建硕の原文はすでに十分簡潔です。書き直すより、そのまま抜き出してください |
Dependencies
- xurl:
xurl whoamiでユーザー名が返ること(認証 OK) - jq:xurl が返す JSON を解析します
~/code/wechat-publish/articles/YYYY-MM-DD-*/article.mdが存在すること:元の記事