Take the compose finished draft and rewrite it into a thread suitable for X. By default, deliver Markdown text in the reply for the client to manually copy and publish; this skill does not call the X API, does not send automatically, and does not modify the original draft.
Client-readable description
What this skill can do
Read the finished draft provided by the client, preserve the core points and necessary evidence, and reorganize it into an X thread with a continuous reading rhythm: the first post is responsible for making people keep reading, the middle posts each complete one logical action, and the final post gives a natural CTA.
| What the client wants | What the skill will do | What the client can see |
|---|---|---|
| Post the article as an X thread | Extract the main line, split the argument, number the posts, and control the length of each post | A set of copyable Markdown tweet entries |
| The draft contains code, commands, or links | Keep code and URLs as-is, and adjust surrounding text if necessary | Untruncated code/links, plus clear notices where safe splitting is not possible |
| Needs to be published to X | Only generates publication text | "Generate text, publish manually"; will not call the X API or send any content |
How the client uses it
Other recognizable phrasings include "post an X" and "send a tweet"; if the target is an X Articles long-form draft, hand it off to soia-media-publish-x-article。
- Say "post as an X thread", "split into a tweet thread", or "thread this", and provide the finished draft, file contents, or path.
- If requested, also specify the target audience, tone, whether to keep the title, the CTA direction, and any code/links that need to be preserved.
- The Agent first confirms the input scope and main line, then outputs the thread; by default it does not overwrite the original draft, and only saves separately when the client specifies a path.
- The client manually copies each post and publishes it to X; the publishing order follows the numbering.
Dependencies and installation
Installation (recommended: install the entire domain plugin to install all skills in this repository at once):
claude plugin marketplace add soia-team/soia-open-skills
claude plugin install soia-media-content@soia
If you only want this one skill, you can use the npx route. Note that the skill will land in the shared source of truth ~/.agents/skills; if the plugin is also installed, the same skill will appear in two indexes and they will drift separately, so it is recommended to choose one of the two:
npx skills add soia-team/soia-open-media-content-skills -g -a '*' -s soia-media-publish-x-thread -y
- This skill is a pure LLM rewriting process, with no scripts, no private configuration, no API key, and no external service dependencies.
soia-media-compose-article-draftis a common upstream artifact, but it is not a hard installation-level dependency; you can also directly provide any finished draft.- Currently does not connect to the X API. Any "publishing" only means generating text; manual publishing is done by the client.
WorkBuddy 's loading unit is role-based experts rather than plugins,npx skills add -a '*' cannot cover it, and it needs to be installed separately, see docs/install/workbuddy.md。
Logging and completion receipt
Each execution must report the actual processing scope, the number of posts, and the manual publishing boundary, and must not describe "generated" as "published". Minimum format:
完成:已将 <输入范围> 改写为 <N> 条 X thread 文本,未调用 X API。
日志摘要:
- started: <输入来源、目标口吻与约束>
- processed: <条目数量;长度检查结果>
- created/updated: <回复文本或客户指定的输出位置>
- skipped/failed: <未处理内容及原因;没有则写“无”>
验证:
- <每条 ≤280 字符、编号连续、代码/链接完整性核对结果>
问题与下一步:
- 请人工按 (1/N) 顺序复制发布;<其它问题,没有则写“无”>
Splitting rules
1. First determine the main line
- Write in one sentence the core judgment this article should make readers remember; every post in the thread must serve this main line.
- Preserve evidence, examples, qualifications, and sources that would change the conclusion; delete repetitive setup, irrelevant side branches, and exaggerated statements that cannot be verified.
- If the original draft has multiple equally important themes, first ask the client to choose the main thread; when risk is low, you may choose the main thread that best represents the original draft and explain this in the receipt.
2. Organize continuous reading
- State the theme and the conflict/benefit clearly in the first item, and use concrete facts, counterintuitive judgments, or questions to create a reason to keep reading; do not merely restate the title.
- Each middle item should carry only one action: raise a question, give a judgment, explain a reason, show an example, add a limitation, or land on a method.
- Each item should be basically understandable even out of context; use natural transitional words to maintain the thread's sense of progression, and avoid mechanically chopping the article's paragraphs into pieces.
- The final item should close with the core judgment and give a CTA that matches the article's content (for example, reply with your experience, save the method, continue the discussion); do not create false urgency.
3. Length and completeness
- Each item includes
(1/N)、(2/N)and other numbering,Nuse the final total number of items; numbering is continuous and no item is missing. - The body of each item must not exceed 280 characters, verified according to X's actual character counting method; numbering, spaces, and punctuation also count toward the length budget.
- Do not break items in the middle of a code block, and do not rewrite commands, variable names, JSON, regex, file paths, or URLs; if code and links are too long, split the surrounding explanation or clearly tell the client that manual handling is required.
- Do not delete negations, units, qualifiers, sources, or key punctuation just to fit within the length limit; if length and completeness cannot both be satisfied, first report the conflict and request a tradeoff.
- Preserve the facts of the original draft, and do not add numbers, cases, quotations, or conclusions not supported by the input.
Output format
The default output is a Markdown list, with one paragraph per item:
- (1/N) <首条抓钩子与主题>
- (2/N) <一个完整的论证动作>
- …
- (N/N) <核心收束 + CTA>
Outside the list, you may add one line: "Length check: passed/needs manual review" and a necessary note on tradeoffs. Do not disguise items as published links, and do not output API call results. If the client has not specified a disk location, produce only the reply text; if a location is specified, then write it to a separate draft file, and never overwrite the source file.
Browser direct-fill mode (optional)
Enable when the client says "fill into X," "save to X drafts," or "post directly."The main route is the built-in Playwright script, host-agnostic—it behaves consistently in Claude Code, Codex, Gemini CLI, opencode, and any other host that can run Python; it does not rely on any host-specific browser tool. Ordinary short posts do not require a Premium subscription.
Main route: scripts/x_post.py (host-agnostic)
Dependencies (one-time):pip install playwright && python -m playwright install chromium。
python3 scripts/x_post.py login # 首次:弹窗人工登录,登录态存本地 profile
python3 scripts/x_post.py status # 检查登录态
python3 scripts/x_post.py draft "(1/2) …" "(2/2) …" # 填入并保存草稿(默认档)
python3 scripts/x_post.py send "文案" --yes # 发布档:仅在授权条件满足时传 --yes
- The script preferentially usesthe real Chrome installed on the system(channel=chrome, with an interface consistent with everyday Chrome; if not installed, it falls back to the bundled Chromium).
- Why one login is still required: Chrome 136+ official security policy prohibits any automation from driving the user's default profile (to prevent cookie theft), and a running Chrome also locks the profile—directly reusing the everyday Chrome login state is technically blocked by the platform. The script therefore uses a separate profile:On first
loginlogin once, then reuse permanently, and it will not repeatedly ask for login. - The login state exists only in the local profile directory (default
~/.config/soia-skills/…/x-profile, can be overridden with--profile-dir/SOIA_X_PROFILE_DIR), does not export cookies, and does not enter repositories or logs; it also does not take the route of "unlocking Keychain to export Chrome cookies"—temporary plaintext cookies are an unacceptable attack surface in a multi-host environment. - The script has three built-in gates: interception when not logged in,
sendmissing--yesrejection, and rejection of a single item over 280 characters. - Output is a single-line JSON receipt (action/parts/url, etc.), machine-readable by any host.
Alternative route: host browser tool
When the host happens to provide browser control (such as Claude Code's claude-in-chrome, which reuses the user's real Chrome login state via a browser extension—this is the only legitimate path to avoid repeated logins), the host tool can be preferred,zero login cost for the client, and adaptability is better when the page is redesigned; the flow and authorization tiers are completely identical to the main route. When there is no host toolmust notuse this as a reason to skip the script main route.
Two authorization tiers, never overstep a tier (applies to both routes)
| Tier | Trigger condition | Final action |
|---|---|---|
| Save draft (default) | The client only says "fill it in/save as draft" | Save to unsent posts; the receipt states the location (More > Unsent posts) |
| Publish directly | The clientgives the content verbatim in this session or confirms the final copy, and explicitly says "send" | Publish and make best efforts to retrieve the tweet URL and write it into the receipt |
Red line: the default tier never publishes; when the content is an AI-rewritten product (not given verbatim by the client), even if the client previously said "send directly," the final copy must first be shown to the client for confirmation before publishing; the publish tier must use the client-confirmed copy verbatim and must not alter it on the client's behalf.
Receipt: the draft tier reports "saved to unsent posts + item count"; the publish tier reports the tweet URL (if not captured, state this truthfully); on mid-way failure (selector redesign/login invalid), truthfully report which step it stopped at.
Verification and testing
Script-layer forward tests (no login state needed, must be run after changing the script):status returns for an empty profile logged_in:false;draft intercepts when not logged in and guides to login;send missing --yes rejection; rejection of a single item >280 characters—all four exit with single-line JSON. Verification with login state:login after draft one single-item and one multi-item run each, manually confirmed in X "Unsent posts";send --yes passing criterion is that the receipt contains the real tweet URL (the publishing chain was verified on a real account on 2026-07-21).