Awesome-agent-skills obsidian-to-x

发布内容和文章到 X (Twitter)。支持常规推文(文字/图片/视频)和 X Articles(长文 Markdown)。使用真实 Chrome 浏览器绕过反机器人检测。当用户说"发推"、"发到 X"、"发到 twitter"、"分享到 X"、"分享到 twitter"、"发 tweet"、"同步到 X"、"发布到 X"、提到"X Articles"、想从 Obsidian 笔记发布长文内容、或需要转换 Obsidian Markdown 到 X 格式时使用。适用于所有 X/Twitter 发布任务。

install
source · Clone the upstream repo
git clone https://github.com/libukai/awesome-agent-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/libukai/awesome-agent-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/obsidian-to-x" ~/.claude/skills/libukai-awesome-agent-skills-obsidian-to-x && rm -rf "$T"
manifest: skills/obsidian-to-x/SKILL.md
source content

Post to X (Twitter)

Posts text, images, videos, and long-form articles to X via real Chrome browser (bypasses anti-bot detection).

Default Behavior (No Additional Instructions)

When user invokes this skill without specifying what to publish (e.g., just says "发到 X" or uses slash command):

  1. Clean Chrome CDP processes first (see CDP Cleanup below)

  2. Get current active file,按优先级依次尝试:

    优先级 1:

    <current_note>
    标签(Claudian 注入,最准确) 检查用户消息末尾是否有
    <current_note>
    标签,直接提取路径,无需执行任何命令

    优先级 2:workspace.json(本地 fallback)

    jq -r '.lastOpenFiles[] | select(endswith(".md"))' .obsidian/workspace.json | head -1
    

    优先级 3:Obsidian CLI(最后手段)

    obsidian file active
    
  3. Read the file content using Read tool

  4. Auto-detect publishing type:

    • Check if file has frontmatter with
      是否长文: true
    • 是否长文: true
      → Publish as X Article (long-form)
    • 是否长文: false
      ,
      是否长文
      absent, or no frontmatter
      → Publish as Regular Post (short-form)
  5. Inform user of detected type and proceed with publishing

  6. Execute appropriate workflow:

    • For X Article: Publish with
      x-article.ts
      (handles Obsidian conversion internally)
    • For Regular Post: Convert with
      md-to-post.ts
      → Publish with
      x-post.ts
  7. Execute publishing scripts via Bash background process (REQUIRED - do NOT use Agent

    run_in_background=true
    ):

    • Always use Bash
      &
      with log redirected to vault
      :
      bun ... > .temp/x-article-output.log 2>&1 &
    • Never use Agent tool with
      run_in_background=true
      — the output file lands in
      /private/tmp/
      which is blocked by vault permission hooks, causing TaskOutput to fail and triggering a duplicate execution
    • Poll progress by reading
      .temp/x-article-output.log
      with
      sleep N && cat .temp/x-article-output.log | tail -30
  8. Success Detection: Check

    .temp/x-article-output.log
    for success markers:

    • Best method: Count
      Image upload verified
      occurrences matching expected image count
    • Alternative: Look for
      Post composed (preview mode)
      or
      Browser remains open
    • For X Articles: Look for
      Article composed
      or
      Browser remains open
    • Use
      sleep 15 && cat .temp/x-article-output.log | tail -30
      to poll
    • Report success immediately when markers detected, don't wait for task completion

Example:

User: "发到 X"
AI: ✓ Detected current file: Articles/news/my-article.md
    ✓ Found `是否文章: true` → Publishing as X Article
    [proceeds with article publishing workflow]

CDP Cleanup

Run this before every publishing script to prevent "Chrome debug port not ready" errors. Do it automatically — no need to ask the user.

pkill -f "Chrome.*remote-debugging-port"; pkill -f "Chromium.*remote-debugging-port"; sleep 2

Content Types

X Articles vs Regular Posts:

FeatureX ArticlesRegular Posts
ContentRich text (Markdown)Plain text only
Formatting✅ Bold, italic, headers, lists❌ All stripped
Code blocks✅ Syntax highlighting❌ Not supported
Images✅ Multiple images✅ Max 4 images
LengthLong-form (unlimited)Short (280 chars)
RequirementsX PremiumFree
Script
x-article.ts
x-post.ts
Conversion
md-to-article.ts
md-to-post.ts

When to use:

  • X Articles: Blog posts, tutorials, technical articles with code
  • Regular Posts: Quick updates, announcements, simple text + images

Quick Start

For Obsidian users who want to publish the currently active article:

bash ${SKILL_DIR}/scripts/publish-active.sh

This automatically:

  1. Detects the active file (via workspace.json or Obsidian CLI)
  2. Converts Obsidian syntax to X format
  3. Opens X Articles editor with content filled in

Script Directory

Important: All scripts are located in the

scripts/
subdirectory of this skill.

Agent Execution Instructions:

  1. Determine this SKILL.md file's directory path as
    SKILL_DIR
  2. Script path =
    ${SKILL_DIR}/scripts/<script-name>.ts
  3. Replace all
    ${SKILL_DIR}
    in this document with the actual path
  4. Resolve
    ${BUN_X}
    runtime: if
    bun
    installed →
    bun
    ; if
    npx
    available →
    npx -y bun
    ; else suggest installing bun

Script Reference:

ScriptPurpose
Publishing Scripts
scripts/x-post.ts
Publish regular posts (text + images, max 4)
scripts/x-video.ts
Publish video posts (text + video)
scripts/x-quote.ts
Publish quote tweet with comment
scripts/x-article.ts
Publish X Articles (rich text + images + code)
Conversion Scripts
scripts/md-to-post.ts
Convert Obsidian Markdown → plain text + images (for Posts)
scripts/md-to-article.ts
Convert Obsidian Markdown → HTML + images (for Articles)
Utilities
scripts/publish-active.sh
One-command publish for active Obsidian file

Prerequisites

  • Google Chrome or Chromium
  • bun
    runtime
  • First run: log in to X manually (session saved)
  • For Obsidian integration:
    jq
    tool (
    brew install jq
    on macOS)

Pre-flight Check (Optional)

Before first use, suggest running the environment check:

${BUN_X} ${SKILL_DIR}/scripts/check-paste-permissions.ts

Checks: Chrome, Bun, Accessibility permissions, clipboard, paste keystroke.

If any check fails, provide fix guidance per item:

CheckFix
ChromeInstall Chrome or set
X_BROWSER_CHROME_PATH
env var
Bun runtime
brew install oven-sh/bun/bun
(macOS) or
npm install -g bun
Accessibility (macOS)System Settings → Privacy & Security → Accessibility → enable terminal app
Paste keystroke (Linux)Install
xdotool
(X11) or
ydotool
(Wayland)

Obsidian Integration

For Obsidian users, this skill can automatically detect the currently active file and convert Obsidian-specific syntax.

Quick workflow:

# One-command publish
bash ${SKILL_DIR}/scripts/publish-active.sh

Manual workflow:

# Step 1: Get active file (workspace.json method, 39x faster)
ACTIVE_FILE=$(jq -r '.lastOpenFiles[0]' .obsidian/workspace.json)

# Step 2: Publish (x-article.ts handles Obsidian conversion internally)
bun ${SKILL_DIR}/scripts/x-article.ts "$ACTIVE_FILE"

For detailed Obsidian integration, see

references/obsidian-integration.md
:

  • How to detect active file (workspace.json vs CLI)
  • Performance comparison (0.007s vs 0.274s)
  • Error handling and fallback strategies

For Obsidian syntax conversion, see

references/obsidian-conversion.md
:

  • Converting
    ![[]]
    image syntax
  • Handling Chinese frontmatter fields
  • Manual conversion commands

Regular Posts

Text + up to 4 images. Plain text only (all Markdown formatting stripped).

From Obsidian Markdown

Step 1: CDP cleanup (see CDP Cleanup section)

Step 2: Convert Markdown to plain text + images

# Extract content from Markdown file
# Supports both standard Markdown ![](path) and Obsidian ![[path]] image syntax
# --output-dir keeps downloaded images inside the vault (.temp) to avoid permission issues
bun ${SKILL_DIR}/scripts/md-to-post.ts "Articles/my-post.md" --output-dir ".temp/x-post" > .temp/post-content.json
TEXT=$(jq -r '.text' .temp/post-content.json)
IMAGES=$(jq -r '.images[]' .temp/post-content.json)

Image Syntax Support:

  • ✅ Standard Markdown:
    ![alt](path/to/image.png)
  • ✅ Obsidian syntax:
    ![[path/to/image.png]]
  • ✅ Network URLs:
    ![alt](https://example.com/image.jpg)
    or
    ![[https://example.com/image.jpg]]
  • Local paths are converted to absolute paths
  • Network images are automatically downloaded in parallel (3-4x faster than sequential)

Step 2.5: 提取封面图作为第一张图

从文件 frontmatter 中读取

封面
属性,若存在则将其路径作为第一张图片:

# 提取 封面 属性(支持 ![[path]] 和裸路径两种格式)
COVER_RAW=$(grep -m1 '^封面:' "Articles/my-post.md" | sed 's/^封面: *//' | tr -d '"')
# 解析 Obsidian wikilink 格式 ![[path]] → path
COVER_PATH=$(echo "$COVER_RAW" | sed 's/^!\[\[//;s/\]\]$//')

# 组合图片列表:封面优先,正文图片补充(总数上限 4 张)
IMAGE_ARGS=""
if [ -n "$COVER_PATH" ]; then
  IMAGE_ARGS="--image \"$COVER_PATH\""
fi
for img in $IMAGES; do
  IMAGE_ARGS="$IMAGE_ARGS --image \"$img\""
done
  • 封面
    属性为空或不存在,则跳过,仅使用正文图片
  • 总图片数量上限 4 张(x-post.ts 限制),超出部分自动截断

Step 3: Publish post

eval "${BUN_X} ${SKILL_DIR}/scripts/x-post.ts \"$TEXT\" $IMAGE_ARGS"

Direct Usage

${BUN_X} ${SKILL_DIR}/scripts/x-post.ts "Hello!" --image ./photo.png

Parameters:

ParameterDescription
<text>
Post content (plain text, positional)
--image <path>
Image file (repeatable, max 4)
--profile <dir>
Custom Chrome profile

Content Processing:

  • ✅ Plain text (all formatting stripped)
  • ✅ Images (max 4)
  • ❌ No rich text formatting
  • ❌ No code blocks
  • ❌ No HTML

Browser Behavior:

  • Script opens browser with content filled in
  • Browser remains open for manual review
  • User can review, edit, and publish at their own pace
  • User manually closes browser when done
  • Add
    --submit
    flag to auto-publish (closes after 2 seconds)

AI Success Detection (for background execution):

  • Don't wait for task completion (browser stays open indefinitely)
  • Best method: Count
    Image upload verified
    in output matching expected image count
  • Alternative: Check for
    Post composed (preview mode)
    or
    Browser remains open
  • Use short timeout (10-15s) then check output content
  • Report success immediately when markers detected
  • Example workflow:
    1. Know you're uploading 3 images
    2. Wait 10-15s for uploads
    3. Check output: grep "Image upload verified" | wc -l
    4. If count == 3 → Report success immediately
    

Video Posts

Text + video file.

Step 1: CDP cleanup (see CDP Cleanup section)

Step 2: Publish video post

${BUN_X} ${SKILL_DIR}/scripts/x-video.ts "Check this out!" --video ./clip.mp4

Parameters:

ParameterDescription
<text>
Post content (positional)
--video <path>
Video file (MP4, MOV, WebM)
--profile <dir>
Custom Chrome profile

Limits: Regular 140s max, Premium 60min. Processing: 30-60s.


Quote Tweets

Quote an existing tweet with comment.

Step 1: CDP cleanup (see CDP Cleanup section)

Step 2: Publish quote tweet

${BUN_X} ${SKILL_DIR}/scripts/x-quote.ts https://x.com/user/status/123 "Great insight!"

Parameters:

ParameterDescription
<tweet-url>
URL to quote (positional)
<comment>
Comment text (positional, optional)
--profile <dir>
Custom Chrome profile

X Articles

Long-form Markdown articles (requires X Premium).

Step 1: CDP cleanup (see CDP Cleanup section)

Step 2: Publish article

${BUN_X} ${SKILL_DIR}/scripts/x-article.ts article.md
${BUN_X} ${SKILL_DIR}/scripts/x-article.ts article.md --cover ./cover.jpg

Parameters:

ParameterDescription
<markdown>
Markdown file (positional)
--cover <path>
Cover image
--title <text>
Override title

Frontmatter:

title
/
标题
,
cover_image
/
封面
/
配图
supported in YAML front matter.

Title Resolution (for X Articles):

# 优先读 frontmatter 的 `标题` 属性,为空则 fallback 到文件名
TITLE=$(obsidian property:read name="标题" path="$NOTE_PATH" 2>/dev/null | tr -d '[:space:]')
if [ -z "$TITLE" ]; then
  TITLE=$(basename "$NOTE_PATH" .md)
fi
# 传入脚本
${BUN_X} ${SKILL_DIR}/scripts/x-article.ts "$NOTE_PATH" --title "$TITLE"

Note: Script opens browser with article filled in. User reviews and publishes manually.

Code Blocks Support

Code blocks are automatically extracted from Markdown and inserted into X Articles editor. Supports all languages (JavaScript, Python, TypeScript, Rust, Go, Shell, etc.). No manual action required.


Troubleshooting

Common issues:

  • Chrome debug port not ready → Always clean CDP processes first (see above)
  • macOS Accessibility Permission Error → Enable in System Settings
  • Code blocks not inserting → Automatic, check console for errors
  • Image temp path outside vault →
    md-to-post.ts
    downloads images to system
    /tmp
    by default, which may be blocked by vault-restricted hooks. Fix: always pass
    --output-dir ".temp/x-post"
    to keep images inside the vault:
    bun ${SKILL_DIR}/scripts/md-to-post.ts "Articles/my-post.md" --output-dir ".temp/x-post"
    

For detailed troubleshooting, see

references/troubleshooting.md
.


References

  • references/obsidian-integration.md
    - Obsidian file detection and integration
  • references/obsidian-conversion.md
    - Converting Obsidian syntax to standard Markdown
  • references/regular-posts.md
    - Regular posts workflow and troubleshooting
  • references/articles.md
    - X Articles detailed guide
  • references/troubleshooting.md
    - Common issues and solutions
  • references/browser-automation-lessons.md
    - Browser automation patterns and lessons learned (CDP, DraftJS, background tabs)

Extension Support

Custom configurations via EXTEND.md. Check these paths (priority order):

  • .libukai-skills/obsidian-to-x/EXTEND.md
    (project directory)
  • $HOME/.libukai-skills/obsidian-to-x/EXTEND.md
    (user home)

EXTEND.md Supports: Default Chrome profile

Notes

  • First run: manual login required (session persists)
  • All scripts fill content into the browser and keep it open for manual review
  • Browser remains open until user manually closes it (except when using
    --submit
    flag)
  • Cross-platform: macOS, Linux, Windows