obsidian-to-x

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

3,893 stars

Best use case

obsidian-to-x is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

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

Teams using obsidian-to-x should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/obsidian-to-x/SKILL.md --create-dirs "https://raw.githubusercontent.com/libukai/awesome-agent-skills/main/skills/obsidian-to-x/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/obsidian-to-x/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How obsidian-to-x Compares

Feature / Agentobsidian-to-xStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

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

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# 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](#cdp-cleanup) below)

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

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

   **优先级 2:workspace.json(本地 fallback)**
   ```bash
   jq -r '.lastOpenFiles[] | select(endswith(".md"))' .obsidian/workspace.json | head -1
   ```

   **优先级 3:Obsidian CLI(最后手段)**
   ```bash
   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.

```bash
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 | `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
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**:
| Script | Purpose |
|--------|---------|
| **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:

```bash
${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 `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**:
```bash
# One-command publish
bash ${SKILL_DIR}/scripts/publish-active.sh
```

**Manual workflow**:
```bash
# 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](#cdp-cleanup) section)

**Step 2: Convert Markdown to plain text + images**

```bash
# 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 中读取 `封面` 属性,若存在则将其路径作为第一张图片:

```bash
# 提取 封面 属性(支持 ![[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**

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

### Direct Usage

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

**Parameters**:
| Parameter | Description |
|-----------|-------------|
| `<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](#cdp-cleanup) section)

**Step 2: Publish video post**

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

**Parameters**:
| Parameter | Description |
|-----------|-------------|
| `<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](#cdp-cleanup) section)

**Step 2: Publish quote tweet**

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

**Parameters**:
| Parameter | Description |
|-----------|-------------|
| `<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](#cdp-cleanup) section)

**Step 2: Publish article**

```bash
${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>` | 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):
```bash
# 优先读 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:
  ```bash
  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

Related Skills

vscode-sftp-config

3893
from libukai/awesome-agent-skills

This skill should be used when setting up SFTP deployment for static websites to production servers, including converting projects from Docker/Express to static hosting, deploying Vue/React/Angular builds, setting up Slidev presentations, or configuring Hugo/Jekyll/Gatsby sites. Use this when the user asks to "setup SFTP deployment", "deploy static site to server", "configure Nginx for static files", "convert from Docker to static hosting", "deploy Vue build to production", "setup subdomain hosting", or "configure SFTP in VS Code". Provides SFTP configuration templates and production-ready Nginx configurations with security headers and caching.

vscode-port-monitor-config

3893
from libukai/awesome-agent-skills

This skill should be used when configuring VS Code Port Monitor extension for development server monitoring. Use when the user asks to "set up port monitoring for Vite", "monitor my dev server ports", "configure port monitor for Next.js", "track which ports are running", "set up multi-port monitoring", "monitor frontend and backend ports", or "check port status in VS Code". Provides ready-to-use configuration templates for Vite (5173), Next.js (3000), and microservices architectures with troubleshooting guidance.

vscode-httpyac-config

3893
from libukai/awesome-agent-skills

Configure VSCode with httpYac for API testing and automation. This skill should be used specifically when converting API documentation to executable .http files (10+ endpoints), setting up authentication flows with pre-request scripts, implementing request chaining with response data, organizing multi-file collections with environment management, or establishing Git-based API testing workflows with CI/CD integration.

mcp-config

3893
from libukai/awesome-agent-skills

Configure MCP (Model Context Protocol) servers for Claude Code. Manage MCP servers at user or project scope with best practices to avoid context pollution.

skill-creator-pro

3893
from libukai/awesome-agent-skills

Create new skills, modify and improve existing skills, and measure skill performance. Enhanced version with quick commands. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. Triggers on phrases like "make a skill", "create a new skill", "build a skill for", "improve this skill", "optimize my skill", "test my skill", "turn this into a skill", "skill description optimization", or "help me create a skill".

plugin-integration-checker

3893
from libukai/awesome-agent-skills

Check if a skill is part of a plugin and verify its integration with commands and agents. Use after creating or modifying a skill to ensure proper plugin architecture. Triggers on "check plugin integration", "verify skill integration", "is this skill in a plugin", "check command-skill-agent integration", or after skill creation/modification when the skill path contains ".claude-plugins" or "plugins/".

obsidian

3891
from openclaw/skills

Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli. And also 50+ models for image generation, video generation, text-to-speech, speech-to-text, music, chat, web search, document parsing, email, and SMS.

Workflow & Productivity

Obsidian CLI 探索记录

3891
from openclaw/skills

Skill for the official Obsidian CLI (v1.12+). Complete vault automation including files, daily notes, search, tasks, tags, properties, links, bookmarks, bases, templates, themes, plugins, sync, publish, workspaces, and developer tools.

Workflow & Productivity

obsidian-notes

3891
from openclaw/skills

Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli.

Workflow & Productivity

obsidian-viz

3891
from openclaw/skills

Generate Obsidian-compatible visualization files (Excalidraw / Mermaid / Canvas). Supports text descriptions and image inputs, outputs editable diagrams in Obsidian or standard formats.

Content & Documentation

obsidian-markdown

31392
from sickn33/antigravity-awesome-skills

Create and edit Obsidian Flavored Markdown with wikilinks, embeds, callouts, properties, and other Obsidian-specific syntax. Use when working with .md files in Obsidian, or when the user mentions wikilinks, callouts, frontmatter, tags, embeds, or Obsidian notes.

obsidian-clipper-template-creator

31392
from sickn33/antigravity-awesome-skills

Guide for creating templates for the Obsidian Web Clipper. Use when you want to create a new clipping template, understand available variables, or format clipped content.