ホーム / 全スキル / コンテンツ制作 / x-article-publisher
コンテンツ制作 · wshuyi/x-article-publisher-skill

x-article-publisher

Markdown記事を適切な書式でX (Twitter) Articlesエディタに公開します。MarkdownファイルやURLをX Articlesに投稿したい場合、「publish to X」「post article to Twitter」「X article」などの指示、またはX Premium記事公開の支援を求める場合に使用します。カバー画像のアップロードを処理し、Markdownをリッチテキストに自動変換します。

L4 高権限 · 段階的認可と監査V1 コミット固定コマンド実行ネットワーク通信ファイル書き込みDual-pass Audit
Author wshuyiGitHub wshuyi/x-article-publisher-skill ↗Stars 867License MIT(仓库根 LICENSE:Copyright (c) 2024 wshuyi;gh api spdx=MIT)Commit eb3197d1d8
管理レベルは Skill の能力と推奨統制措置を示すものであり、品質や安全性の認証、利用可否の判定ではありません。使用の可否はユーザーおよび管理者が決定します。Agent 環境によって制約が異なるため、自動保護を仮定しないでください。第三者製 Skill の著作権は原著作者に帰属し、利用リスクは利用者が負います。

1仕組みと動作原理

3段階のパイプライン:ローカルのPythonスクリプトがMarkdownファイルを構造化データ(JSON + ペースト用HTML)に解析し、ホスト提供のPlaywright MCPブラウザツールがコンテンツをX Articlesエディタに流し込み、最終的に下書き保存のみで終了します。スクリプト層とブラウザ層の責務が分離されており、スクリプト側はネットワーク通信を一切行いません。

README.md
Markdown File ↓ Python parsing Structured Data (title, images with block_index, HTML) ↓ Playwright MCP X Articles Editor (browser automation) ↓ Draft Saved (never auto-publishes)
README冒頭のアーキテクチャ図が示す通り、解析・投入・下書き保存の3ステップが本Skillの全機能であり、バックエンドサーバーやX APIは存在しません。

ブロック単位の分割があらゆる位置特定機能の基盤:split_into_blocksが行ごとにスキャンし、コードブロック(___CODE_BLOCK_START/ENDで囲まれ、未閉じのフォールバックを含む)、'---' 区切り線、'#'/'>' で始まる見出しと引用、独立行の画像をそれぞれ独立ブロックとして切り出し、その他の連続行を段落ブロックとしてマージします。

skills/x-article-publisher/scripts/parse_markdown.py
def split_into_blocks(markdown: str) -> list[str]: """Split markdown into logical blocks (paragraphs, headers, quotes, code blocks, etc.)."""
Markdown解析ライブラリを使用せず、標準ライブラリの正規表現とステートマシンのみで実装されています。ブロック順序が後続のblock_indexの座標系となります。

画像と区切り線は解析段階で本文から分離:extract_images_and_dividersが各ブロックで___DIVIDER___または画像正規表現を判定し、block_indexとして収集済みクリーンブロック数を記録、前ブロックの最終行の先頭80文字をafter_textとして取得します。分離後の本文のみがHTML変換へと渡されます。

skills/x-article-publisher/scripts/parse_markdown.py
block_index = len(clean_blocks) after_text = "" if clean_blocks: prev_block = clean_blocks[-1].strip() lines = [l for l in prev_block.split('\n') if l.strip()] after_text = lines[-1][:80] if lines else ""
インデックスは「このブロックの後ろに挿入する」という意味を持ちます。after_textは人間による目視確認専用です。

タイトル抽出には明確な優先順位と重複排除ルールが存在:extract_titleは最初のH1、H2、最初の非画像・非空行を順に検索します。タイトルがH1から取得された場合、本文内に再度タイトルが出現するのを防ぐため、その行をMarkdownから削除します。

skills/x-article-publisher/scripts/parse_markdown.py
# Remove H1 title line from markdown to avoid duplication
SKILL.mdのCritical Rule 7「H1はタイトルとしてのみ扱い本文には含めない」に対応しています。

リッチテキスト変換はMarkdownライブラリではなく順序依存の正規表現置換を使用:コードブロックをまず<blockquote>に変換(Xが<pre><code>を未サポートのため)、続いてH2/H3→h2/h3、**→strong、*→em、[text](url)→a、>→blockquote、- および 1.→li をulで囲み、最後に空行で段落分割、段落内改行を<br>に変換して<p>で囲みます。

skills/x-article-publisher/scripts/parse_markdown.py
# Convert to blockquote format since X Articles doesn't support <pre><code>
順序が極めて重要です。コードブロックのマーカーを先に処理しないと、ブロック内の*や*が強調構文として誤認識されます。

クリップボードへの格納はサードパーティ製ライブラリではなくOSネイティブAPIを使用:macOS環境ではpyobjcのNSPasteboardを使用し、clearContentsの後にsetData_forType_でHTML型を書き込み、同時にsetString_forType_でプレーンテキストのコピーも保持するため、XエディタへのCmd+VでH2・太字・リンクなどのリッチテキストが維持されます。

skills/x-article-publisher/scripts/copy_to_clipboard.py
pasteboard.setData_forType_(ns_data, NSPasteboardTypeHTML) # Also set plain text version pasteboard.setString_forType_(html, NSPasteboardTypeString)
Windows環境での対応実装はclip-utilのClipboard()["html"]です。両環境ともにpyperclipは使用していません。

クリップボードへの画像格納時に任意で圧縮とプラットフォーム形式のラッピングを実施:compress_imageがPillowで画像を開き、必要に応じてRGB変換、thumbnailで(2000,2000)以内に制限した上で指定JPEG qualityでBytesIOに書き込みます。Windows環境ではさらにBMPとして保存し、14バイトのBITMAPFILEHEADERを切り落として残りのDIBデータをCF_DIBに格納します。

skills/x-article-publisher/scripts/copy_to_clipboard.py
data = output.getvalue()[14:] # Skip BITMAPFILEHEADER
このコメントは、WindowsパスでBMPヘッダーを手動で削除しなければならない理由(クリップボードが生のDIBのみを受け付けるため)を説明しています。

テーブルの画像化は完全な自前描画:table_to_image.pyがMarkdownテーブルを独自に解析(:---配置マーカーを認識)し、Pillow ImageDrawのtextbboxで列幅を計測、角丸枠線・ヘッダー背景色・行区切り線を描画します。macOS(sfns/pingfang/stheiti)→Linux(dejavu)→Windows(arial/msyh)の順でTrueTypeフォントを試し、すべて失敗した場合のみビットマップフォントへフォールバックします。

skills/x-article-publisher/scripts/table_to_image.py
for path in font_paths: try: return ImageFont.truetype(path, size) except (OSError, IOError): continue
フォント候補にPingFangやメイリオ・微软雅黑(C:/Windows/Fonts/msyh.ttc)が含まれており、中国語・日本語テーブルへの配慮が見られます。

2コア機能

01Markdownのブロックレベル解析およびblock_index座標データの出力(タイトル/カバー画像/本文画像/区切り線/total_blocks)
02Markdown→HTMLリッチテキスト変換(H2/H3、太字、斜体、リンク、引用、順序付き/なしリスト、引用ブロックへのコードブロック降格)
03システムクリップボードへのリッチテキスト書き込み(macOS NSPasteboard HTML+String、Windows clip-util html)
04システムクリップボードへの画像書き込みと任意圧縮(--qualityでJPEG圧縮をトリガー、--max-width/--max-heightで2000×2000に制限)
05MarkdownテーブルのPNG画像変換(ヘッダー背景色、角丸枠線、列配置、--scale高DPI倍率対応)
06区切り線(---)の検出と独立インデックス化(AgentがXの「Insert > Divider」メニュー経由で挿入するため)
07画像パスのフォールバック検索:カレントに見つからない場合、ファイル名をもとに~/Downloads、~/Desktop、~/Picturesを順次検索し、置換をstderrに通知
08タイトルおよびフロントマターの処理:YAMLフロントマターの自動スキップ、H1のタイトル昇格と本文からの削除

3外部依存関係

KindDependency / Endpoint
clipython3(解释器,脚本 shebang 与文档命令均为 python3/python)
climmdc(@mermaid-js/mermaid-cli,Mermaid→PNG,需 npm 全局安装)
clipkill(排障步骤里杀 Playwright MCP 相关进程)
packagePillow(image 压缩、BMP/DIB 封装、表格自绘)
packagepyobjc-framework-Cocoa(macOS AppKit/Foundation,NSPasteboard)
packagepywin32(win32clipboard/CF_DIB)与 clip-util(Windows HTML 剪贴板)
networkPlaywright MCP(宿主浏览器自动化工具集 browser_navigate/browser_click/browser_press_key 等;skill 不自带浏览器驱动)
networkX Articles 编辑器(被自动化访问的站点,读取用户已登录会话)
apiX (Twitter) Premium Plus 订阅(Articles 功能的准入前提,非 API 调用)

4推奨統制措置と判定根拠

安全運用のための推奨統制措置
Agent 向け
段階的・都度認可、サンドボックス推奨
管理者向け
承認フロー、ロールバック可能、全履歴監査
判定根拠(ファイルパスと抜粋)
  • scripts/: ローカルスクリプトまたはコマンドの実行: python3 skills/x-article-publisher/scripts/parse_markdown.py <markdown_file
  • 网络端点: 外部ネットワークアクセスの宣言: スクリプト層:なし——3つのスクリプトすべてでrequests/urllib.request/httpx/socket呼び出しは皆無
  • 本地工作目录: ファイルの読み書きおよび成果物の出力: /tmp/article_html.html(SKILL.mdにてAgentにシェルリダイレクト経由での書き込みを指示)
監査ノート: オレンジ判定。3点のソースコード証拠:① ユーザーのログイン済みアカウントセッションへの明示的依存——SKILL.mdに「If login needed, prompt user to log in manually.」、docs/GUIDE.md 3.4 注意事項に「2. **已登录 X**:ブラウザでXアカウントにログイン済みであることを確認」とあり、自動化全体がユーザーの認証状態下で実行され、Cookie/セッション機能の実質的な利用に該当(Skill自身が読み取るわけではない);② バージョン固定なしのサードパーティ製ライブラリおよびグローバルnpmパッケージへの依存——plugin.jsonにPython依存としてPillow/pyobjc-framework-Cocoa、SKILL.mdに「npm install -g @mermaid-js/mermaid-cli」とあり、リポジトリ内にrequirements.txtが存在しない;③ ユーザー環境の改変——copy_to_clipboard.pyの「pasteboard.clearContents()」や「win32clipboard.EmptyClipboard()」によるクリップボードの消去と上書き、table_to_image.pyの「img.save(output, 'PNG')」によるファイル出力、docs/GUIDE.mdにある~/.claude/settings.jsonの読み取りやpkillによるプロセス終了コマンドの提示。レッド判定に至らない理由:スクリプト内にeval/exec/subprocess/os.systemがなく任意コード実行面が存在しないこと、TLS検証の無効化や対スクレイピング回避、大量データ収集がなく、唯一のネットワーク接続先がホスト側のMCPブラウザを介したX公式ドメインであること。

5二重目の独立検証

  • [ok] 全部 evidence.quote 逐字存在性(Python 子串校验,覆盖 SKILL.md/两个 README/GUIDE/三个脚本/plugin.json/LICENSE) — 所有引用均为原文精确子串,未发现 paraphrase 冒充 quote;含反斜杠的正则引用按源码原样保留。
  • [ok] 网络调用反查(requests|urllib|httpx|socket|aiohttp|curl|wget|api.x.com|api.twitter) — 仅 parse_markdown.py 的 `import urllib.parse` 与 `urllib.parse.unquote(img_path)` 命中,用途是 URL 解码文件名;无任何 HTTP 客户端。
  • [discrepancy] 凭证/密钥反查(token|api_key|environ|getenv|.env|cookie|oauth|Bearer) — 脚本层零命中,确认无凭证读取;但文档层 docs/GUIDE.md 指示 `cat ~/.claude/settings.json | grep playwright`,会读取可能含 API key 的 Claude Code 配置文件——第一遍未计入,已在 security.credential_reads 补记。
  • [ok] 隐藏文件与额外可执行体反查(find 全清单 + .gitignore) — 仓库仅 13 个非 .git 条目:3 个 py 脚本、SKILL.md、GUIDE.md、两个 README、plugin.json、LICENSE、.gitignore、skills/、docs/、.claude-plugin/;无隐藏脚本、无二进制、无混淆内容。
  • [ok] 外部依赖调用点复核(Pillow / pyobjc / pywin32 / clip-util / mmdc / pkill / Playwright MCP) — Pillow 在 copy_to_clipboard.py 与 table_to_image.py 的 import 行可定位;mmdc 与 pkill 只出现在 SKILL.md 文档指令中,仓库无脚本调用它们;Playwright MCP 只在 plugin.json 与 SKILL.md 工具名中出现,skill 不自带驱动。
  • [discrepancy] 功能声明 vs 实际能力:frontmatter 宣称 'publish a Markdown file/URL to X Articles' — 夸大:parse_markdown.py 只有 `with open(filepath, 'r', encoding='utf-8') as f:` 与 `os.path.exists(args.file)`,仅接受本地文件,全仓库无 URL 抓取分支。若 agent 照字面接受 URL 输入,会自行引入不受控的网页内容。
  • [discrepancy] 定位策略一致性:SKILL.md Step 6 推荐 after_text 文本搜索 vs Critical Rules 第 5 条与 README 声称的 block_index — SKILL.md 自身矛盾:Step 6 标题写 '(Text Search Positioning)' 且正文称『推荐方法: 使用 after_text 文字搜索定位,比 block_index 更直观可靠』,而同一文件 Critical Rules 写『5. Block index positioning - Use block_index for precise image/divider placement』,README 又称 v1.1 已用 block_index 替换文本匹配。agent 执行时选错策略会导致图片错位。
  • [discrepancy] 版本一致性:plugin.json version 与 README/changelog 版本号、日期 — plugin.json 为 version 1.1.0,README 顶部却写 v1.2.0;changelog 条目为 v1.2.0 (2025-01)、v1.1.0 (2025-12)、v1.0.0 (2025-12),v1.2.0 的月份早于 v1.1.0,与 pin commit 日期 2026-01-25 也对不上,疑为文档笔误。

6結論

  • 公式API依存ゼロ、X開発者アカウントの取得やOAuth審査を回避可能
  • 唯一の不可逆アクション(公開操作)が自動化スコープから明示的に除外
  • あいまいなテキスト一致に依存しない確定的かつ正確な位置特定
  • コア解析スクリプトのサードパーティ依存ゼロにより、監査および再利用が容易
  • 自前描画によるテーブル画像化処理で、マルチバイト文字を含む表に対応
  • 最適な用途: X Premium Plusに加入済みで、普段Markdownで執筆しており、ブラウザ操作をAgentに任せたい個人に最適:タイトル・画像・引用・リストを含む日英・中英記事をMarkdownからX Articlesの下書きへ変換し、書式や配置の計算をスクリプトで行い、人間は最終プレビューと公開ボタンの押下のみを担当できます。「ローカルスクリプトによる確定的変換+MCPブラウザによる配信」という半自動パイプラインのリファレンスとしても適しています。macOS環境が最も整備されています(plugin.jsonではmacosのみ宣言)。Windowsでのリッチテキストクリップボード品質はclip-utilに依存します。
    不向きな用途: ワンクリックでの完全自動公開を求める用途(設計上、常に下書きで停止);Linux環境のユーザー(copy_to_clipboardがUnsupported platformを明示的にスローし、READMEでも未完成と記載);X Premium Plus未加入のアカウント;複雑なネスト構文・ネイティブテーブル・数式を多用した記事(テーブルやMermaidは手動での画像化が必要、コードブロックは引用ブロックへ降格);信頼できない外部Markdownをそのまま投入し高い再現性を求める用途(HTMLエスケープ処理の欠如、位置特定ドキュメント内の自己矛盾);配信経路の監査証跡が必要なチーム(クリップボードおよびブラウザセッションレベルの操作履歴は記録・再生不可)。
    Install Agent-Ready
    ① Verified Mirror 2026-09-15
    Option A · Direct DownloadDownload x-article-publisher.tar.gz
    sha256: 61755757ff05c8f7…
    Option B · Copy Guide for Agent
    install.json
    Agent はこの JSON ガイドから自動的にダウンロードして検証します。
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit eb3197d1d8.