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 发布任务。
git clone https://github.com/libukai/awesome-agent-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"
skills/obsidian-to-x/SKILL.mdPost 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):
-
Clean Chrome CDP processes first (see CDP Cleanup below)
-
Get current active file,按优先级依次尝试:
优先级 1:
标签(Claudian 注入,最准确) 检查用户消息末尾是否有<current_note>
标签,直接提取路径,无需执行任何命令。<current_note>优先级 2:workspace.json(本地 fallback)
jq -r '.lastOpenFiles[] | select(endswith(".md"))' .obsidian/workspace.json | head -1优先级 3:Obsidian CLI(最后手段)
obsidian file active -
Read the file content using Read tool
-
Auto-detect publishing type:
- Check if file has frontmatter with
是否长文: true
→ Publish as X Article (long-form)是否长文: true
,是否长文: false
absent, or no frontmatter → Publish as Regular Post (short-form)是否长文
- Check if file has frontmatter with
-
Inform user of detected type and proceed with publishing
-
Execute appropriate workflow:
- For X Article: Publish with
(handles Obsidian conversion internally)x-article.ts - For Regular Post: Convert with
→ Publish withmd-to-post.tsx-post.ts
- For X Article: Publish with
-
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
— the output file lands inrun_in_background=true
which is blocked by vault permission hooks, causing TaskOutput to fail and triggering a duplicate execution/private/tmp/ - Poll progress by reading
with.temp/x-article-output.logsleep N && cat .temp/x-article-output.log | tail -30
- Always use Bash
-
Success Detection: Check
for success markers:.temp/x-article-output.log- Best method: Count
occurrences matching expected image countImage upload verified - Alternative: Look for
orPost composed (preview mode)Browser remains open - For X Articles: Look for
orArticle composedBrowser remains open - Use
to pollsleep 15 && cat .temp/x-article-output.log | tail -30 - Report success immediately when markers detected, don't wait for task completion
- Best method: Count
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:
| Feature | X Articles | Regular Posts |
|---|---|---|
| Content | Rich text (Markdown) | Plain text only |
| Formatting | ✅ Bold, italic, headers, lists | ❌ All stripped |
| Code blocks | ✅ Syntax highlighting | ❌ Not supported |
| Images | ✅ Multiple images | ✅ Max 4 images |
| Length | Long-form (unlimited) | Short (280 chars) |
| Requirements | X Premium | Free |
| Script | | |
| Conversion | | |
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:
- Detects the active file (via workspace.json or Obsidian CLI)
- Converts Obsidian syntax to X format
- 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:
- Determine this SKILL.md file's directory path as
SKILL_DIR - Script path =
${SKILL_DIR}/scripts/<script-name>.ts - Replace all
in this document with the actual path${SKILL_DIR} - Resolve
runtime: if${BUN_X}
installed →bun
; ifbun
available →npx
; else suggest installing bunnpx -y bun
Script Reference:
| Script | Purpose |
|---|---|
| Publishing Scripts | |
| Publish regular posts (text + images, max 4) |
| Publish video posts (text + video) |
| Publish quote tweet with comment |
| Publish X Articles (rich text + images + code) |
| Conversion Scripts | |
| Convert Obsidian Markdown → plain text + images (for Posts) |
| Convert Obsidian Markdown → HTML + images (for Articles) |
| Utilities | |
| One-command publish for active Obsidian file |
Prerequisites
- Google Chrome or Chromium
runtimebun- First run: log in to X manually (session saved)
- For Obsidian integration:
tool (jq
on macOS)brew install jq
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:
| Check | Fix |
|---|---|
| Chrome | Install Chrome or set env var |
| Bun runtime | (macOS) or |
| Accessibility (macOS) | System Settings → Privacy & Security → Accessibility → enable terminal app |
| Paste keystroke (Linux) | Install (X11) or (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  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:
 - ✅ Obsidian syntax:
![[path/to/image.png]] - ✅ Network URLs:
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:
| Parameter | Description |
|---|---|
| Post content (plain text, positional) |
| Image file (repeatable, max 4) |
| 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
flag to auto-publish (closes after 2 seconds)--submit
AI Success Detection (for background execution):
- Don't wait for task completion (browser stays open indefinitely)
- Best method: Count
in output matching expected image countImage upload verified - Alternative: Check for
orPost composed (preview mode)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:
| Parameter | Description |
|---|---|
| Post content (positional) |
| Video file (MP4, MOV, WebM) |
| 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:
| Parameter | Description |
|---|---|
| URL to quote (positional) |
| Comment text (positional, optional) |
| 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:
| Parameter | Description |
|---|---|
| Markdown file (positional) |
| Cover image |
| 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 →
downloads images to systemmd-to-post.ts
by default, which may be blocked by vault-restricted hooks. Fix: always pass/tmp
to keep images inside the vault:--output-dir ".temp/x-post"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
- Obsidian file detection and integrationreferences/obsidian-integration.md
- Converting Obsidian syntax to standard Markdownreferences/obsidian-conversion.md
- Regular posts workflow and troubleshootingreferences/regular-posts.md
- X Articles detailed guidereferences/articles.md
- Common issues and solutionsreferences/troubleshooting.md
- Browser automation patterns and lessons learned (CDP, DraftJS, background tabs)references/browser-automation-lessons.md
Extension Support
Custom configurations via EXTEND.md. Check these paths (priority order):
(project directory).libukai-skills/obsidian-to-x/EXTEND.md
(user home)$HOME/.libukai-skills/obsidian-to-x/EXTEND.md
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
flag)--submit - Cross-platform: macOS, Linux, Windows