Core Principles
Only create drafts. Unless the user explicitly confirms it can be publicly published, do not click X's final 发布 button.
Use an independent Playwright browser session; do not seize the Chrome window the user is currently using. The Chrome login state can be reused, but only via a temporarily exported Playwright cookie JSON.
Quick Start
- If
/tmp/x_current_cookies.jsondoes not exist or the login state is invalid, first export the current X cookies:
python3 ~/.codex/skills/yichen-x-article-draft-uploader/scripts/export_x_cookies_from_chrome.py --output /tmp/x_current_cookies.json
- Before actually touching X, first dry-run parse the article to confirm the cover, the number of body images, and the anchors:
python3 ~/.codex/skills/yichen-x-article-draft-uploader/scripts/upload_markdown_to_x_article.py \
"/absolute/path/to/article.md" \
--cookies-json /tmp/x_current_cookies.json \
--dry-run
If the dry-run warns that "the article's first valid content is not an image," stop first and remind the user: it is recommended to add a cover image at the very beginning of the article. Do not continue uploading. Only when the user explicitly refuses to add a cover and asks to continue uploading a draft without a cover, add --allow-no-cover:
python3 ~/.codex/skills/yichen-x-article-draft-uploader/scripts/upload_markdown_to_x_article.py \
"/absolute/path/to/article.md" \
--cookies-json /tmp/x_current_cookies.json \
--allow-no-cover
- Create a clean new X Article draft and upload:
python3 ~/.codex/skills/yichen-x-article-draft-uploader/scripts/upload_markdown_to_x_article.py \
"/absolute/path/to/article.md" \
--cookies-json /tmp/x_current_cookies.json
The script will output these result files:
- Draft URL:
/tmp/x_article_upload_url.txt - Validation result JSON:
/tmp/x_article_upload_result.json - Final screenshot:
/tmp/x_article_final_uploaded.png
Workflow
- Use this Skill's built-in
scripts/parse_markdown.pyto parse Markdown. - Check whether the article's first valid content is an image. If it is not an image, by default interrupt the upload and remind the user to add a cover image; only when the user explicitly refuses to add a cover and asks to continue, use
--allow-no-coverto skip cover upload. - Treat the first image at the very beginning of the article as the cover image. When using
--allow-no-cover, do not upload a cover; all images in the article are treated as body images. - From the original Markdown, read the line before each body image as the image insertion anchor. Pay special attention in list scenarios: use the actual previous line, for example
Git 变化。, not the longer fallback assembled from preceding list items. - Launch a brand-new Playwright Chromium context and add the X cookies.
- Open
https://x.com/compose/articles, clickcreate, and record the newly generated/compose/articles/edit/...URL。 - Upload the cover via the file input in the cover area, then click X's
应用. If this step is skipped, X will leave a media-edit mask blocking the editor, and the cover will not actually be saved. - Fill in the title, and paste the rich HTML body into
[data-testid="composer"]。 - Insert body images from back to front. Process each image in this order:
- Find the highest-priority anchor in the current editor.
- Click at the end of that paragraph.
- Press
End, then pressEnter。 - Paste the image file via a clipboard paste event.
- Wait for the media count detected on the page to increase before continuing to the next image.
- Wait for X autosave to complete, then validate the title, the beginning/end of the body, the absence of
MPH_MARKER, and confirm that the total media count equals封面数 + expected_image_count。
Common Blockers
- If X redirects to
/login, the login state is invalid; re-export the cookies. - If the article does not start with an image, by default stop the upload. Remind the user to add a cover image; only after the user explicitly refuses, use
--allow-no-coverContinue. - If the editor is blocked by an overlay after uploading the cover, find the
应用button and click it. - Do not use a hidden file input directly for body images. It may point to the cover uploader and cause the cover to be replaced.
- Do not only look at the media count. Especially for images after the list, check whether the anchor is correct. The script will record in the result JSON
anchor_usedandexpected_anchor。 - If a run is only partially successful but the image positions are wrong, prioritize creating a clean new draft and rerunning rather than forcing fixes on the old draft.
Script description
upload_markdown_to_x_article.py --dry-runis a safety check and will not open X.--allow-no-coveris only used when the user explicitly refuses to add a cover image; it skips the cover upload and inserts all images as body images.- The upload script requires Python Playwright and a valid X cookie JSON.
- The Markdown parser is already built into the current Skill and does not depend on the old
x-article-publisherSkill。 - The cookie export script only reads local Chrome cookies and writes them to the specified temporary Playwright cookie file; the Skill itself does not save any cookies.