SuperAPI AI Directory

soia-pkm-publish-x-article

Writing & research

View repository

Updated 10-16 08:45

About this skill

soia-pkm-publish-x-article

Push Obsidian/local Markdown finished drafts directly to the X Articles drafts, completing the publish family's X long-form output (publish-x-thread produces thread text, this skill produces rich-text long-form drafts).

Client-readable description

What this skill can do

Client wants Skill will do Client can see
Post a Markdown long-form article to X Articles Parse title/cover/body images → create a draft in the browser → paste rich text → insert images in place → mechanical validation Real-time browser operation process, draft URL, validation checklist, and final receipt
The article has no cover image Stop and remind (an X Article without a cover looks bad), only continue without a cover after explicit consent Reminders and confirmation questions
Missing dependency/not logged in/no Premium+ Stop and clearly point out what is missing Login/sign-up guidance, do not fill in any credentials on the user's behalf

Security bottom line: only save the draft,never click "Publish"; the login session stays only in the browser, do not export cookies, do not write any credentials to disk.

How the client uses it

  1. Say "post <file> as an X Article"; make sure the browser is logged in to X and the account has Premium+ (Articles permission).
  2. The Agent first runs a dry-run parse and reports: title, cover, number of body images, missing image list; if the cover is missing, ask you first.
  3. The browser phase is fully visible; after completion give the draft URL, and you manually review and publish it.

Dependencies and installation

npx skills add soia-team/soia-open-skills -g -s soia-pkm-publish-x-article -y
  • macOS + Python 3.9+ (pure stdlib; clipboard via osascript, image downsampling via the built-in sips)
  • Choose one for the browser side:claude-in-chrome(recommended, reuses your real Chrome's X login session) or the built-in Browser pane (you need to log in to X in it the first time)
  • The X account needs Premium+(Articles is a paid feature)
  • No private config.yml: this skill does not touch any key/cookie

Logs and completion receipt

After execution it must output: what was done, draft URL, validation results (item by item), skipped/failed items, next step (manual review and publish).

Positioning and loop closure

compose-article-draft produces finished text → this skill pushes X Articles drafts (equivalent to publish-wechat-draft for WeChat Official Accounts). "Only create drafts, never publish" is the same security philosophy as on the WeChat Official Accounts side.

Triggers

User says Action
"post to X Article" "upload X article" "push to X drafts" Full workflow (parse → upload → validate)
"First see whether this article can be posted to X" Only run the parse dry-run, report without touching the browser

Workflow

Step 1: Parse (without touching the browser)

python3 scripts/parse_x_article.py <article.md> > /tmp/x_article.json
python3 scripts/parse_x_article.py <article.md> --html-only > /tmp/x_article_body.html

JSON fields:title / cover_image(only when the article starts with an image) / content_images[](contains block_index and after_text)/ dividers[] / html / missing_images。

Two gates, pass them before touching the browser:

  1. missing_images > 0 → stop, give the user the list of missing images (you can use --search-dir to specify the directory to search for them).
  2. cover_image is null → stop, remind them "X Article looks bad without a cover, suggest adding an image at the top"; only continue if the user explicitly says not to add one (in that case treat all images as body images).

Step 2: Enter the editor

  1. Browser choice: prefer claude-in-chrome (real Chrome already logged in); otherwise the Browser pane (if not logged in, have the user log in,do not enter credentials on their behalf)。
  2. Navigate to https://x.com/compose/articles——what loads is thedrafts listpage, not the editor; directly click the "create/write" button to enter the editor (do not wait for "Add title" to appear, it only renders after the click).
  3. Redirect to login page = not logged in; no Articles entry on the page = no Premium+. In both cases stop and tell the user.

Step 3: Cover + title

  1. Has cover: click the cover upload control and use file upload to upload cover_image ; X will pop up a media editing layer, **you must click "Apply"** to close it, otherwise the editor is blocked by the overlay and the cover won't take effect.
  2. Title box (placeholder "Add title") enter title。

Step 4: Paste rich text into the body

python3 scripts/clipboard_x.py html --file /tmp/x_article_body.html

Click the editor body area → Cmd+V. Headings/bold/links/lists/quotes are preserved by the HTML clipboard flavor. After pasting, spot-check whether the text of the first paragraph and the last paragraph are both present.

Step 5: Insert images and dividers (all by block_index from largest to smallestreverse order)

For each content_images[i](reverse order):

python3 scripts/clipboard_x.py image <path> --max-bytes 3000000
  1. Use after_text to find the target paragraph in the editor, click it.
  2. Press the End keyto push the cursor to the end of the paragraph (to avoid clicking into a link within the paragraph).
  3. Cmd+V paste → wait for "Uploading media/Uploading" to disappear before inserting the next one.

Divider (dividers[], also reverse order): X ignores <hr>, you must click the corresponding position and then use the editor's "Insert > Divider" menu.

Reason for reverse order: the editor is a dynamic document, inserting earlier ones first will move all subsequent anchors.

Step 6: Mechanical verification (not complete unless all pass)

Check item Criterion
Title Editor title == JSON title
Body first/last sentence both can be found in the editor
Media count Number of images in the editor == cover(0/1) + content_images count
Divider count == dividers count
Draft status "Saved" autosave marker appears; record the draft URL

Step 7: Receipt

Draft URL + item-by-item results of the verification checklist + "Please review manually and click publish yourself".Under no circumstances click publish.

Format support boundaries

Element Handling
H2/H3, bold, italic, links, ordered/unordered lists, quotes Preserved natively
Code block Convert to a quote block (X does not support <pre>)
Tables / mermaid X does not support it: first manually convert to a PNG image and insert it, or accept the degraded loss; the parser passes the text through as-is
--- Divider Menu insertion (see step 5)
H1 Only as a heading, not in the body

Boundaries and exceptions

Scenario Handling
Redirect to login page Stop, have the user log in in the browser and retry; do not touch credentials
No Articles entry Stop, indicate X Premium+ is required
Missing image Step 1 gate intercepts, provide a checklist
No cover Gate reminder, continue only after explicit consent
Formatting lost after pasting Check the clipboard script output; retry once; if it still fails, report the actual result, do not falsely claim success
Image upload gets stuck Wait for the upload indicator to disappear; if it times out, take a screenshot and report, do not blindly continue
Controls cannot be found after an X editor redesign Stop and report the actual page state, do not guess at clicks

Script specifications (scripts/)

Script Responsibility Dependencies
parse_x_article.py MD → JSON (title/cover/image/divider/HTML); adapted from wshuyi/x-article-publisher-skill (MIT, see THIRD_PARTY_NOTICES in the repository) stdlib
clipboard_x.py HTML/image to clipboard (osascript;--max-bytes automatically downsample to a temporary copy when over the limit) stdlib + macOS osascript/sips

Verification and testing

Script-layer fixture forward test (no X account needed, must run after changing scripts):

  1. Create a test article containing frontmatter title, a cover image at the top of the article, 2 body images (png+jpg),--- divider, code blocks, ordered/unordered lists, and links.
  2. parse_x_article.py Assertions:title taken from frontmatter;cover_image is an absolute path and points to the top-of-article image;content_images[].block_index increments,after_text has no ___CODE___ marker and has had markdown notation stripped; after deleting the top-of-article image, cover_image is null (gate semantics).
  3. clipboard_x.py html after, osascript -e 'clipboard info' must contain «class HTML»;image produce for png/jpg respectively «class PNGf»/JPEG picture;--max-bytes when over the limit, use a sips temporary copy andthe original file's byte count remains unchanged。

Browser end-to-end (requires a real X Premium+ login session): run the full process with the above test article, with all green on the step 6 verification checklist + a draft URL as the pass criteria; when control changes are caused by an X editor redesign, update this document's selector descriptions based on the actual page state.

Completion receipt

  1. What was done: one sentence + draft URL.
  2. Verification checklist: item-by-item results for step 6; mark any failed items truthfully.
  3. Next step: publish on your own after manual review; recommended to use soia-pkm-cover-image to generate the cover.