Communitygithub.com

Gamma-Software/post-to-x-skill

Post a tweet or thread to X/Twitter via the real web UI (Playwright, no paid API) — a Claude Code / agent skill

post-to-x-skill란 무엇인가요?

post-to-x-skill is a Claude Code agent skill that post a tweet or thread to X/Twitter via the real web UI (Playwright, no paid API) — a Claude Code / agent skill.

지원 대상Claude Code~Codex CLI~Cursor
npx skills add Gamma-Software/post-to-x-skill

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Post to X

Publishes a tweet or thread to X/Twitter by driving x.com in a real browser, not the API. X removed the free API in Feb 2026 (pay-per-use: $0.015/post, $0.20 with a link), so this reuses a browser session you log into once by hand and posts through the site itself. It uses the real Chrome binary + strips the automation flags to get past X's "browser not trusted" check.

Login once (manual) → parse text/thread → compose in a headed dry-run → [user OK] → post for real

ToS + risk. Automated posting is against X's ToS and can get an account flagged. This is for low-volume, human-paced self-posting — a release thread, a build-in-public update — not a bulk bot. Always dry-run first, keep delays human, and never post on the user's behalf without an explicit go.

0. One-time setup

The script needs Playwright and Google Chrome installed (Chrome is the trusted binary; Playwright only supplies the driver — no browser download).

cd <skill-dir> && npm install          # installs playwright into the skill

Then authenticate once — opens real Chrome; the user logs in by hand (password + 2FA), the session persists to ~/.config/post-to-x/x-profile:

node scripts/post-x.mjs login

If channel: "chrome" errors, Chrome isn't found — install Google Chrome (not just Chromium). The login window waits up to 5 min for a logged-in state.

1. Prepare the content

Accept either inline text or a file:

  • Single tweet--text "…".
  • Thread file--file <path>. Tweet boundaries are auto-detected:
    1. **N/M** markers per tweet (build-in-public x-thread.md style), else
    2. tweets separated by a line containing only ---, else
    3. no separators → the whole file is one tweet.
  • Images — per tweet, add a line _image: path_ or ![](path) inside that tweet; or attach to the first tweet with repeatable --image <path>. X takes up to 4 per tweet. Paths are relative to the file.
  • Auto-stripped: > … blockquotes (DRAFT banners), # … headings, _(…chars…)_ notes.

Keep each tweet ≤ 280 chars — the script prints the count and flags any over.

2. Dry-run (mandatory gate)

Always compose visibly without sending first, so the user sees the real thread in the X composer:

node scripts/post-x.mjs post --file thread.md --headed --dry-run

It types every tweet into the compose modal (adding thread posts via the "+" button), attaches images, then holds 30 s and closes — nothing is posted. Show the user; get an explicit OK.

3. Post for real

Only after the user approves, drop --dry-run:

node scripts/post-x.mjs post --file thread.md          # or --text "…"
node scripts/post-x.mjs post --file thread.md --image shot.png --headed

--headed shows the browser; default is headless. The script clicks X's "Post all" (thread) / "Post" (single) and confirms the composer closed.

4. Validate

  • Parse gate--parse-only prints the parsed tweets + char counts + image paths and exits. Run it whenever the file format is non-obvious.
  • Dry-run gate — the headed compose above is the real proof it will post correctly; skipping it has shipped a broken thread before (ambiguous selectors, wrong tweet split).
  • Session gate — if the composer never loads, the session expired → re-run login.

5. Report

Tell the user: how many tweets posted (or composed, for a dry-run), any tweet over 280, images attached, and the profile dir used. If a selector failed, say which step and that X's UI likely changed (the data-testids in post-x.mjs are the brittle part).

Notes

  • Selectors used: tweetTextarea_<i>, addButton, fileInput, tweetButton — scoped to the compose [role="dialog"]. Update these in scripts/post-x.mjs if X reworks the composer.
  • The session lives in ~/.config/post-to-x/x-profile (override --profile). It's outside the repo — never commit it.
  • X only; LinkedIn/Mastodon/Bluesky have free APIs — use a scheduler (Postiz, Buffer, Typefully) for those instead of scraping.

관련 스킬