Initial commit: Hermes Agent skills collection
- Trading skills (OKX, dividend, lottery, quantitative) - Creative skills (ASCII art, diagrams, video) - Development skills (GitHub, debugging, TDD) - Research skills (arXiv, blog monitoring) - Productivity skills (email, documents, notes) - MCP integration skills - Custom user skills
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
---
|
||||
description: Skills for interacting with social platforms and social-media workflows — posting, reading, monitoring, and account operations.
|
||||
---
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
name: xiaohongshu-style
|
||||
description: 小红书发文风格指南。生成符合平台调性、规避风控的投资/理财/生活类内容。当用户说"发小红书"、"小红书风格"、"做成小红书内容"时使用。
|
||||
version: 1.1.0
|
||||
tags: [xiaohongshu, social-media, content, finance]
|
||||
---
|
||||
|
||||
# 小红书发文风格指南
|
||||
|
||||
## 核心调性
|
||||
- **人设**:真实分享者,不是专家/老师。用"我自己在用"、"分享一下"口吻
|
||||
- **语气**:轻松活泼,像跟朋友聊天。可用感叹号但不过度
|
||||
- **结构**:短段落 + 分隔线 + emoji标记,每段2-3行
|
||||
- **长度**:正文控制在 **500-650字**,用户明确要求"简洁"。1000字太长没人看。
|
||||
- **标题**:最多 **20字**(小红书限制),含emoji。例:"💰月投7500|高息股阶梯买入法"
|
||||
|
||||
## 排版规范
|
||||
- 用 ━━━ 分隔大段落
|
||||
- 用 emoji 数字 1️⃣2️⃣3️⃣ 做列表
|
||||
- 每段开头用 emoji 点缀(💡📊🔖🎯💰📈)
|
||||
- 关键数据用 | 或 | 分隔
|
||||
- 结尾必有互动引导 + 免责声明 + 标签
|
||||
|
||||
## 风控红线(必须遵守)
|
||||
1. **不提具体股票代码/名称** → 用"某XX龙头"、"某银行股"代替
|
||||
2. **不说"推荐/建议买入"** → 用"分享思路/我的做法"
|
||||
3. **不承诺收益** → 用"按X%算≈"、"预估",必须加免责
|
||||
4. **不写具体金额** → 不写"月投7500"、"年息6300"等数字,用"每月闲钱"、"年息还不错"代替。只写百分比(股息率、PE、PB),不写绝对金额
|
||||
5. **不用"稳赚/保本/无风险"** → 违反广告法+平台规则
|
||||
6. **不引导加群/私聊/外链** → 纯内容分享,评论区自然互动
|
||||
7. **免责声明必放** → "以上纯属个人记录,不构成投资建议"
|
||||
|
||||
## 互动设计
|
||||
- 结尾问句引导评论:"你们有在做XX的吗?评论区聊聊"
|
||||
- "懂的评论区聊~" 制造信息差吸引互动
|
||||
- 适当留悬念引发好奇
|
||||
|
||||
## 发布流程 (opencli)
|
||||
|
||||
```bash
|
||||
# 发布笔记(需要 Chrome + opencli 浏览器扩展已连接)
|
||||
opencli xiaohongshu publish "<正文>" --title "<标题>" --images "img1.png,img2.png" --topics "高息股,港股,美股"
|
||||
|
||||
# 保存为草稿(不直接发布)
|
||||
opencli xiaohongshu publish "<正文>" --title "<标题>" --images "img1.png" --draft
|
||||
|
||||
# 检查连接状态
|
||||
opencli doctor
|
||||
```
|
||||
|
||||
**⚠️ PITFALL: Browser Bridge 未连接**
|
||||
如果 `opencli doctor` 显示 `[MISSING] Extension: not connected`,自动发布不可用。
|
||||
需要:Chrome 安装 opencli 扩展 → 开启开发者模式 → 连接。
|
||||
**Fallback**: 准备好内容+图片,让用户手动发布。
|
||||
|
||||
## 配图生成流程
|
||||
|
||||
1. 写 HTML 卡片(单卡单文件,宽 420px)
|
||||
2. 用 browser 工具截图(`browser_navigate` → `browser_vision`,截图自动保存)
|
||||
3. 复制到 `~/.hermes/output/xhs_images/`
|
||||
4. ⚠️ **chromium snap 有 AppArmor 限制**,`--screenshot` 写文件会 permission denied。用 browser 工具代替。
|
||||
5. 每张卡片单独一个 HTML 文件,避免截到多余空白。
|
||||
|
||||
## 配图风格(小红书卡片)
|
||||
- **工具**:生成 HTML 卡片截图,深色背景 + 亮色点缀
|
||||
- **色调**:深蓝/深灰底 + 金色/绿色/橙色数据
|
||||
- **字体**:大标题醒目,数据突出,正文简洁
|
||||
- **布局**:单图信息量要大,像"一张图讲清楚"
|
||||
- **必备元素**:标题、核心数据、emoji、免责声明小字
|
||||
|
||||
### 卡片模板类型
|
||||
1. **数据卡**:核心数字大字体 + 对比数据
|
||||
2. **流程卡**:步骤1→2→3,箭头连接
|
||||
3. **对比卡**:左右/上下对比两种方案
|
||||
4. **清单卡**:勾选列表,每项一行
|
||||
|
||||
### HTML卡片代码模式
|
||||
```html
|
||||
<!-- 深色背景 + 金色数据 -->
|
||||
<div style="background:#1a1a2e;color:#fff;padding:40px;font-family:sans-serif;border-radius:20px">
|
||||
<h2 style="color:#f0c040">📊 标题</h2>
|
||||
<div style="font-size:48px;color:#00d4aa">7.5%</div>
|
||||
<p style="color:#aaa;font-size:12px">免责声明...</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 标签策略
|
||||
- 8-15个标签
|
||||
- 前3个放核心关键词(#高息股 #港股 #美股)
|
||||
- 中间放场景词(#月薪理财 #被动收入)
|
||||
- 最后放泛标签(#理财 #投资)
|
||||
- 标签之间空格分隔,不用逗号
|
||||
|
||||
## 支持文件
|
||||
|
||||
- `templates/xhs_card_template.html` — 深色卡片模板(数据卡+持仓卡),可直接复用修改
|
||||
- `references/publishing-workflow.md` — opencli 发布命令详解 + 故障排查
|
||||
|
||||
## 典型结构模板
|
||||
|
||||
```
|
||||
[吸睛开头 - 1-2句痛点/亮点]
|
||||
|
||||
━━━━━━━━━━━━━━
|
||||
[核心内容1 - 简洁]
|
||||
[核心数据]
|
||||
━━━━━━━━━━━━━━
|
||||
[核心内容2 - 方法/步骤]
|
||||
━━━━━━━━━━━━━━
|
||||
[个人经验/纪律]
|
||||
━━━━━━━━━━━━━━
|
||||
⚠️ 免责声明
|
||||
💬 互动引导
|
||||
🏷️ #标签
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
# 小红书发布工作流
|
||||
|
||||
## opencli 发布命令
|
||||
|
||||
```bash
|
||||
opencli xiaohongshu publish "<content>" \
|
||||
--title "<标题>" \
|
||||
--images "path1.png,path2.png" \
|
||||
--topics "标签1,标签2,标签3" \
|
||||
--draft # 可选:先存草稿
|
||||
```
|
||||
|
||||
### 参数约束
|
||||
- `title`: 最多 20 字(含 emoji)
|
||||
- `content`: 笔记正文,支持换行
|
||||
- `images`: 逗号分隔,最多 9 张,支持 jpg/png/gif/webp
|
||||
- `topics`: 逗号分隔,不含 # 号
|
||||
- `--draft`: true/false,存草稿不直接发布
|
||||
|
||||
### 前置条件
|
||||
1. Chrome 已安装 opencli 浏览器扩展
|
||||
2. 已登录小红书创作者中心
|
||||
3. `opencli doctor` 显示 Extension: connected
|
||||
|
||||
### 故障排查
|
||||
```
|
||||
opencli doctor
|
||||
```
|
||||
- `[MISSING] Extension: not connected` → Chrome 扩展未安装/未启用
|
||||
- `BROWSER_CONNECT` 错码 → 需要重启 daemon: `opencli daemon restart`
|
||||
- 如果扩展已装但连不上 → Chrome 需要保持打开状态
|
||||
|
||||
## 配图生成
|
||||
|
||||
### 流程
|
||||
1. 写 HTML 卡片(单卡单文件,width: 420px)
|
||||
2. 用 Hermes browser 工具截图:
|
||||
- `browser_navigate(url='file:///tmp/card.html')`
|
||||
- `browser_vision(question='截图')` → 自动保存 screenshot_path
|
||||
3. 截图路径在 `/home/openclaw/.hermes/cache/screenshots/` 下
|
||||
4. 复制到 `~/.hermes/output/xhs_images/` 供 opencli 使用
|
||||
|
||||
### ⚠️ chromium snap 限制
|
||||
snap 版 chromium 的 `--screenshot` 参数受 AppArmor 限制,写文件会 permission denied。
|
||||
**不要用** `chromium-browser --headless --screenshot=...`。
|
||||
**用** Hermes 内置 browser 工具(browser_navigate + browser_vision)。
|
||||
|
||||
### 卡片设计规范
|
||||
- 宽度 420px,深色背景 (#1a1a2e → #16213e 渐变)
|
||||
- 金色 (#f0c040) 标题/强调
|
||||
- 青绿 (#00d4aa) 数据
|
||||
- 圆角 24px,内边距 36px 28px
|
||||
- 免责声明小字 10px #555
|
||||
@@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><meta charset="utf-8"><style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{background:#0d1117;display:flex;flex-direction:column;align-items:center;padding:20px;font-family:-apple-system,'PingFang SC','Noto Sans SC',sans-serif}
|
||||
.card{width:420px;background:linear-gradient(135deg,#1a1a2e 0%,#16213e 100%);border-radius:24px;padding:36px 28px;color:#fff;position:relative;overflow:hidden;margin-bottom:30px}
|
||||
.card::before{content:'';position:absolute;top:-50%;right:-50%;width:100%;height:100%;background:radial-gradient(circle,rgba(0,212,170,0.08) 0%,transparent 70%)}
|
||||
.tag{display:inline-block;background:rgba(0,212,170,0.15);color:#00d4aa;padding:4px 12px;border-radius:20px;font-size:12px;margin-bottom:16px}
|
||||
h1{font-size:22px;line-height:1.4;margin-bottom:20px;color:#f0f0f0}
|
||||
.section{margin:18px 0;padding:16px;background:rgba(255,255,255,0.04);border-radius:14px;border-left:3px solid #f0c040}
|
||||
.section-title{color:#f0c040;font-size:14px;font-weight:700;margin-bottom:10px}
|
||||
.row{display:flex;justify-content:space-between;align-items:center;padding:6px 0}
|
||||
.label{color:#8b949e;font-size:13px}
|
||||
.value{color:#00d4aa;font-size:15px;font-weight:700}
|
||||
.value-warn{color:#f0c040;font-size:15px;font-weight:700}
|
||||
.big-num{text-align:center;margin:20px 0}
|
||||
.big-num .num{font-size:52px;font-weight:900;background:linear-gradient(135deg,#00d4aa,#f0c040);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
||||
.big-num .unit{font-size:16px;color:#8b949e;margin-top:4px}
|
||||
.ladder{display:flex;gap:8px;margin:12px 0}
|
||||
.step{flex:1;text-align:center;padding:12px 4px;border-radius:12px;background:rgba(255,255,255,0.06)}
|
||||
.step-n{font-size:11px;color:#8b949e;margin-bottom:4px}
|
||||
.step-pct{font-size:22px;font-weight:900;color:#f0c040}
|
||||
.step-label{font-size:11px;color:#666;margin-top:4px}
|
||||
.divider{height:1px;background:rgba(255,255,255,0.08);margin:16px 0}
|
||||
.footer{font-size:10px;color:#555;line-height:1.6;margin-top:20px}
|
||||
.stock-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:10px 0}
|
||||
.stock-item{padding:10px;background:rgba(255,255,255,0.04);border-radius:10px;text-align:center}
|
||||
.stock-flag{font-size:20px}
|
||||
.stock-name{font-size:12px;color:#ccc;margin-top:2px}
|
||||
.stock-yield{font-size:18px;font-weight:900;color:#00d4aa}
|
||||
.stock-sub{font-size:10px;color:#666}
|
||||
</style></head><body>
|
||||
|
||||
<!-- 卡片1: 数据主题卡 -->
|
||||
<div class="card">
|
||||
<div class="tag">💰 懒人收息法</div>
|
||||
<h1>月投7500港币<br>高息股阶梯买入</h1>
|
||||
<div class="big-num"><div class="num">7.5%</div><div class="unit">平均股息率</div></div>
|
||||
<div class="ladder">
|
||||
<div class="step"><div class="step-n">第1档</div><div class="step-pct">40%</div><div class="step-label">现价买入</div></div>
|
||||
<div class="step" style="border:1px solid rgba(240,192,64,0.3)"><div class="step-n">第2档</div><div class="step-pct">30%</div><div class="step-label">小跌加仓</div></div>
|
||||
<div class="step"><div class="step-n">第3档</div><div class="step-pct">30%</div><div class="step-label">大跌抄底</div></div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="section">
|
||||
<div class="section-title">📊 收息预估</div>
|
||||
<div class="row"><span class="label">年投</span><span class="value">90,000 港币</span></div>
|
||||
<div class="row"><span class="label">第二年起年息</span><span class="value-warn">≈ 6,300</span></div>
|
||||
<div class="row"><span class="label">第五年累计股息</span><span class="value-warn">≈ 22,000+</span></div>
|
||||
</div>
|
||||
<div class="footer">⚠️ 个人记录,不构成投资建议 · 股市有风险</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片2: 持仓清单卡 -->
|
||||
<div class="card">
|
||||
<div class="tag">📋 我的DCA组合</div>
|
||||
<h1>7只标的 · 3档阶梯</h1>
|
||||
<div style="color:#f0c040;font-size:13px;font-weight:700;margin:16px 0 8px">🇭🇰 港股(防守型)</div>
|
||||
<div class="stock-grid">
|
||||
<div class="stock-item"><div class="stock-flag">🏦</div><div class="stock-name">某保险龙头</div><div class="stock-yield">5.4%</div><div class="stock-sub">PE<7 破净</div></div>
|
||||
<div class="stock-item"><div class="stock-flag">🏛️</div><div class="stock-name">某银行龙头</div><div class="stock-yield">6.9%</div><div class="stock-sub">PE 7 破净</div></div>
|
||||
</div>
|
||||
<div class="stock-grid" style="margin-top:8px">
|
||||
<div class="stock-item"><div class="stock-flag">📡</div><div class="stock-name">某电信央企</div><div class="stock-yield">6.1%</div><div class="stock-sub">破净</div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div style="color:#f0c040;font-size:13px;font-weight:700;margin:16px 0 8px">🇺🇸 美股(收息型)</div>
|
||||
<div class="stock-grid">
|
||||
<div class="stock-item"><div class="stock-flag">🏠</div><div class="stock-name">某mREIT</div><div class="stock-yield">13.2%</div><div class="stock-sub">PE 7.7</div></div>
|
||||
<div class="stock-item"><div class="stock-flag">🚀</div><div class="stock-name">某科技BDC</div><div class="stock-yield">10.5%</div><div class="stock-sub">PE 8.5</div></div>
|
||||
</div>
|
||||
<div class="stock-grid" style="margin-top:8px">
|
||||
<div class="stock-item"><div class="stock-flag">💼</div><div class="stock-name">某最大BDC</div><div class="stock-yield">10.2%</div><div class="stock-sub">PE 11.7</div></div>
|
||||
<div class="stock-item"><div class="stock-flag">📱</div><div class="stock-name">某电信巨头</div><div class="stock-yield">4.9%</div><div class="stock-sub">PE 7.5</div></div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="section" style="border-left-color:#00d4aa">
|
||||
<div class="section-title" style="color:#00d4aa">🎯 5条纪律</div>
|
||||
<div style="font-size:13px;color:#ccc;line-height:2">① 每月固定操作 ② 没到价位不动<br>③ 股息复投 ④ 只选盈利高分红<br>⑤ 港美股各半</div>
|
||||
</div>
|
||||
<div class="footer">⚠️ 个人记录,不构成投资建议 · 💬 评论区交流</div>
|
||||
</div>
|
||||
|
||||
</body></html>
|
||||
@@ -0,0 +1,432 @@
|
||||
---
|
||||
name: xurl
|
||||
description: "X/Twitter via xurl CLI: post, search, DM, media, v2 API."
|
||||
version: 1.1.1
|
||||
author: xdevplatform + openclaw + Hermes Agent
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
prerequisites:
|
||||
commands: [xurl]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [twitter, x, social-media, xurl, official-api]
|
||||
homepage: https://github.com/xdevplatform/xurl
|
||||
upstream_skill: https://github.com/openclaw/openclaw/blob/main/skills/xurl/SKILL.md
|
||||
---
|
||||
|
||||
# xurl — X (Twitter) API via the Official CLI
|
||||
|
||||
`xurl` is the X developer platform's official CLI for the X API. It supports shortcut commands for common actions AND raw curl-style access to any v2 endpoint. All commands return JSON to stdout.
|
||||
|
||||
Use this skill for:
|
||||
- posting, replying, quoting, deleting posts
|
||||
- searching posts and reading timelines/mentions
|
||||
- liking, reposting, bookmarking
|
||||
- following, unfollowing, blocking, muting
|
||||
- direct messages
|
||||
- media uploads (images and video)
|
||||
- raw access to any X API v2 endpoint
|
||||
- multi-app / multi-account workflows
|
||||
|
||||
This skill replaces the older `xitter` skill (which wrapped a third-party Python CLI). `xurl` is maintained by the X developer platform team, supports OAuth 2.0 PKCE with auto-refresh, and covers a substantially larger API surface.
|
||||
|
||||
---
|
||||
|
||||
## Secret Safety (MANDATORY)
|
||||
|
||||
Critical rules when operating inside an agent/LLM session:
|
||||
|
||||
- **Never** read, print, parse, summarize, upload, or send `~/.xurl` to LLM context.
|
||||
- **Never** ask the user to paste credentials/tokens into chat.
|
||||
- The user must fill `~/.xurl` with secrets manually on their own machine. In Docker, this must be the `~` seen by Hermes tool subprocesses; see the Docker note below.
|
||||
- **Never** recommend or execute auth commands with inline secrets in agent sessions.
|
||||
- **Never** use `--verbose` / `-v` in agent sessions — it can expose auth headers/tokens.
|
||||
- To verify credentials exist, only use: `xurl auth status`.
|
||||
|
||||
Forbidden flags in agent commands (they accept inline secrets):
|
||||
`--bearer-token`, `--consumer-key`, `--consumer-secret`, `--access-token`, `--token-secret`, `--client-id`, `--client-secret`
|
||||
|
||||
App credential registration and credential rotation must be done by the user manually, outside the agent session. After credentials are registered, the user authenticates with `xurl auth oauth2` — also outside the agent session. Tokens persist to `~/.xurl` in YAML. Each app has isolated tokens. OAuth 2.0 tokens auto-refresh.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Pick ONE method. On Linux, the shell script or `go install` are the easiest.
|
||||
|
||||
```bash
|
||||
# Shell script (installs to ~/.local/bin, no sudo, works on Linux + macOS)
|
||||
curl -fsSL https://raw.githubusercontent.com/xdevplatform/xurl/main/install.sh | bash
|
||||
|
||||
# Homebrew (macOS)
|
||||
brew install --cask xdevplatform/tap/xurl
|
||||
|
||||
# npm
|
||||
npm install -g @xdevplatform/xurl
|
||||
|
||||
# Go
|
||||
go install github.com/xdevplatform/xurl@latest
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
xurl --help
|
||||
xurl auth status
|
||||
```
|
||||
|
||||
If `xurl` is installed but `auth status` shows no apps or tokens, the user needs to complete auth manually — see the next section.
|
||||
|
||||
---
|
||||
|
||||
## One-Time User Setup (user runs these outside the agent)
|
||||
|
||||
These steps must be performed by the user directly, NOT by the agent, because they involve pasting secrets. Direct the user to this block; do not execute it for them.
|
||||
|
||||
1. Create or open an app at https://developer.x.com/en/portal/dashboard
|
||||
2. Set the redirect URI to `http://localhost:8080/callback`
|
||||
3. Copy the app's Client ID and Client Secret
|
||||
4. Register the app locally (user runs this):
|
||||
```bash
|
||||
xurl auth apps add my-app --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
|
||||
```
|
||||
5. Authenticate (specify `--app` to bind the token to your app):
|
||||
```bash
|
||||
xurl auth oauth2 --app my-app
|
||||
```
|
||||
(This opens a browser for the OAuth 2.0 PKCE flow.)
|
||||
|
||||
If X returns a `UsernameNotFound` error or 403 on the post-OAuth `/2/users/me` lookup, pass your handle explicitly (xurl v1.1.0+):
|
||||
```bash
|
||||
xurl auth oauth2 --app my-app YOUR_USERNAME
|
||||
```
|
||||
This binds the token to your handle and skips the broken `/2/users/me` call.
|
||||
6. Set the app as default so all commands use it:
|
||||
```bash
|
||||
xurl auth default my-app
|
||||
```
|
||||
7. Verify:
|
||||
```bash
|
||||
xurl auth status
|
||||
xurl whoami
|
||||
```
|
||||
|
||||
After this, the agent can use any command below without further setup. OAuth 2.0 tokens auto-refresh.
|
||||
|
||||
> **Common pitfall:** If you omit `--app my-app` from `xurl auth oauth2`, the OAuth token is saved to the built-in `default` app profile — which has no client-id or client-secret. Commands will fail with auth errors even though the OAuth flow appeared to succeed. If you hit this, re-run `xurl auth oauth2 --app my-app` and `xurl auth default my-app`.
|
||||
|
||||
> **Docker HOME pitfall:** In the official Hermes Docker layout, `/opt/data` is `HERMES_HOME`, but Hermes tool subprocesses use `/opt/data/home` as `HOME`. That means `~/.xurl` resolves to `/opt/data/home/.xurl` for Hermes-run `xurl` commands, not `/opt/data/.xurl`. Run the user setup with the same HOME:
|
||||
> ```bash
|
||||
> HOME=/opt/data/home xurl auth apps add my-app --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
|
||||
> HOME=/opt/data/home xurl auth oauth2 --app my-app YOUR_USERNAME
|
||||
> HOME=/opt/data/home xurl auth default my-app YOUR_USERNAME
|
||||
> HOME=/opt/data/home xurl auth status
|
||||
> ```
|
||||
> If `HOME=/opt/data xurl auth status` succeeds but `HOME=/opt/data/home xurl auth status` shows no apps or tokens, Hermes tool calls will not see the credentials.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Action | Command |
|
||||
| --- | --- |
|
||||
| Post | `xurl post "Hello world!"` |
|
||||
| Reply | `xurl reply POST_ID "Nice post!"` |
|
||||
| Quote | `xurl quote POST_ID "My take"` |
|
||||
| Delete a post | `xurl delete POST_ID` |
|
||||
| Read a post | `xurl read POST_ID` |
|
||||
| Search posts | `xurl search "QUERY" -n 10` |
|
||||
| Who am I | `xurl whoami` |
|
||||
| Look up a user | `xurl user @handle` |
|
||||
| Home timeline | `xurl timeline -n 20` |
|
||||
| Mentions | `xurl mentions -n 10` |
|
||||
| Like / Unlike | `xurl like POST_ID` / `xurl unlike POST_ID` |
|
||||
| Repost / Undo | `xurl repost POST_ID` / `xurl unrepost POST_ID` |
|
||||
| Bookmark / Remove | `xurl bookmark POST_ID` / `xurl unbookmark POST_ID` |
|
||||
| List bookmarks / likes | `xurl bookmarks -n 10` / `xurl likes -n 10` |
|
||||
| Follow / Unfollow | `xurl follow @handle` / `xurl unfollow @handle` |
|
||||
| Following / Followers | `xurl following -n 20` / `xurl followers -n 20` |
|
||||
| Block / Unblock | `xurl block @handle` / `xurl unblock @handle` |
|
||||
| Mute / Unmute | `xurl mute @handle` / `xurl unmute @handle` |
|
||||
| Send DM | `xurl dm @handle "message"` |
|
||||
| List DMs | `xurl dms -n 10` |
|
||||
| Upload media | `xurl media upload path/to/file.mp4` |
|
||||
| Media status | `xurl media status MEDIA_ID` |
|
||||
| List apps | `xurl auth apps list` |
|
||||
| Remove app | `xurl auth apps remove NAME` |
|
||||
| Set default app | `xurl auth default APP_NAME [USERNAME]` |
|
||||
| Per-request app | `xurl --app NAME /2/users/me` |
|
||||
| Auth status | `xurl auth status` |
|
||||
|
||||
Notes:
|
||||
- `POST_ID` accepts full URLs too (e.g. `https://x.com/user/status/1234567890`) — xurl extracts the ID.
|
||||
- Usernames work with or without a leading `@`.
|
||||
|
||||
---
|
||||
|
||||
## Command Details
|
||||
|
||||
### Posting
|
||||
|
||||
```bash
|
||||
xurl post "Hello world!"
|
||||
xurl post "Check this out" --media-id MEDIA_ID
|
||||
xurl post "Thread pics" --media-id 111 --media-id 222
|
||||
|
||||
xurl reply 1234567890 "Great point!"
|
||||
xurl reply https://x.com/user/status/1234567890 "Agreed!"
|
||||
xurl reply 1234567890 "Look at this" --media-id MEDIA_ID
|
||||
|
||||
xurl quote 1234567890 "Adding my thoughts"
|
||||
xurl delete 1234567890
|
||||
```
|
||||
|
||||
### Reading & Search
|
||||
|
||||
```bash
|
||||
xurl read 1234567890
|
||||
xurl read https://x.com/user/status/1234567890
|
||||
|
||||
xurl search "golang"
|
||||
xurl search "from:elonmusk" -n 20
|
||||
xurl search "#buildinpublic lang:en" -n 15
|
||||
```
|
||||
|
||||
For X Articles, use raw API mode instead of the `read` shortcut. `xurl read`
|
||||
expects a post ID or post URL; do not put `read` before a `/2/tweets/...`
|
||||
endpoint. Request the `article` tweet field and ingest `data.article.plain_text`
|
||||
from the JSON response:
|
||||
|
||||
```bash
|
||||
xurl --app APP_NAME '/2/tweets/2057909493250539891?expansions=author_id,attachments.media_keys,referenced_tweets.id&tweet.fields=created_at,lang,public_metrics,context_annotations,entities,possibly_sensitive,conversation_id,in_reply_to_user_id,referenced_tweets,article'
|
||||
```
|
||||
|
||||
### Users, Timeline, Mentions
|
||||
|
||||
```bash
|
||||
xurl whoami
|
||||
xurl user elonmusk
|
||||
xurl user @XDevelopers
|
||||
|
||||
xurl timeline -n 25
|
||||
xurl mentions -n 20
|
||||
```
|
||||
|
||||
### Engagement
|
||||
|
||||
```bash
|
||||
xurl like 1234567890
|
||||
xurl unlike 1234567890
|
||||
|
||||
xurl repost 1234567890
|
||||
xurl unrepost 1234567890
|
||||
|
||||
xurl bookmark 1234567890
|
||||
xurl unbookmark 1234567890
|
||||
|
||||
xurl bookmarks -n 20
|
||||
xurl likes -n 20
|
||||
```
|
||||
|
||||
### Social Graph
|
||||
|
||||
```bash
|
||||
xurl follow @XDevelopers
|
||||
xurl unfollow @XDevelopers
|
||||
|
||||
xurl following -n 50
|
||||
xurl followers -n 50
|
||||
|
||||
# Another user's graph
|
||||
xurl following --of elonmusk -n 20
|
||||
xurl followers --of elonmusk -n 20
|
||||
|
||||
xurl block @spammer
|
||||
xurl unblock @spammer
|
||||
xurl mute @annoying
|
||||
xurl unmute @annoying
|
||||
```
|
||||
|
||||
### Direct Messages
|
||||
|
||||
```bash
|
||||
xurl dm @someuser "Hey, saw your post!"
|
||||
xurl dms -n 25
|
||||
```
|
||||
|
||||
### Media Upload
|
||||
|
||||
```bash
|
||||
# Auto-detect type
|
||||
xurl media upload photo.jpg
|
||||
xurl media upload video.mp4
|
||||
|
||||
# Explicit type/category
|
||||
xurl media upload --media-type image/jpeg --category tweet_image photo.jpg
|
||||
|
||||
# Videos need server-side processing — check status (or poll)
|
||||
xurl media status MEDIA_ID
|
||||
xurl media status --wait MEDIA_ID
|
||||
|
||||
# Full workflow
|
||||
xurl media upload meme.png # returns media id
|
||||
xurl post "lol" --media-id MEDIA_ID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Raw API Access
|
||||
|
||||
The shortcuts cover common operations. For anything else, use raw curl-style mode against any X API v2 endpoint:
|
||||
|
||||
```bash
|
||||
# GET
|
||||
xurl /2/users/me
|
||||
|
||||
# POST with JSON body
|
||||
xurl -X POST /2/tweets -d '{"text":"Hello world!"}'
|
||||
|
||||
# DELETE / PUT / PATCH
|
||||
xurl -X DELETE /2/tweets/1234567890
|
||||
|
||||
# Custom headers
|
||||
xurl -H "Content-Type: application/json" /2/some/endpoint
|
||||
|
||||
# Force streaming
|
||||
xurl -s /2/tweets/search/stream
|
||||
|
||||
# Full URLs also work
|
||||
xurl https://api.x.com/2/users/me
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Global Flags
|
||||
|
||||
| Flag | Short | Description |
|
||||
| --- | --- | --- |
|
||||
| `--app` | | Use a specific registered app (overrides default) |
|
||||
| `--auth` | | Force auth type: `oauth1`, `oauth2`, or `app` |
|
||||
| `--username` | `-u` | Which OAuth2 account to use (if multiple exist) |
|
||||
| `--verbose` | `-v` | **Forbidden in agent sessions** — leaks auth headers |
|
||||
| `--trace` | `-t` | Add `X-B3-Flags: 1` trace header |
|
||||
|
||||
---
|
||||
|
||||
## Streaming
|
||||
|
||||
Streaming endpoints are auto-detected. Known ones include:
|
||||
|
||||
- `/2/tweets/search/stream`
|
||||
- `/2/tweets/sample/stream`
|
||||
- `/2/tweets/sample10/stream`
|
||||
|
||||
Force streaming on any endpoint with `-s`.
|
||||
|
||||
---
|
||||
|
||||
## Output Format
|
||||
|
||||
All commands return JSON to stdout. Structure mirrors X API v2:
|
||||
|
||||
```json
|
||||
{ "data": { "id": "1234567890", "text": "Hello world!" } }
|
||||
```
|
||||
|
||||
Errors are also JSON:
|
||||
|
||||
```json
|
||||
{ "errors": [ { "message": "Not authorized", "code": 403 } ] }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Post with an image
|
||||
```bash
|
||||
xurl media upload photo.jpg
|
||||
xurl post "Check out this photo!" --media-id MEDIA_ID
|
||||
```
|
||||
|
||||
### Reply to a conversation
|
||||
```bash
|
||||
xurl read https://x.com/user/status/1234567890
|
||||
xurl reply 1234567890 "Here are my thoughts..."
|
||||
```
|
||||
|
||||
### Search and engage
|
||||
```bash
|
||||
xurl search "topic of interest" -n 10
|
||||
xurl like POST_ID_FROM_RESULTS
|
||||
xurl reply POST_ID_FROM_RESULTS "Great point!"
|
||||
```
|
||||
|
||||
### Check your activity
|
||||
```bash
|
||||
xurl whoami
|
||||
xurl mentions -n 20
|
||||
xurl timeline -n 20
|
||||
```
|
||||
|
||||
### Multiple apps (credentials pre-configured manually)
|
||||
```bash
|
||||
xurl auth default prod alice # prod app, alice user
|
||||
xurl --app staging /2/users/me # one-off against staging
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Non-zero exit code on any error.
|
||||
- API errors are still printed as JSON to stdout, so you can parse them.
|
||||
- Auth errors → have the user re-run `xurl auth oauth2` outside the agent session.
|
||||
- Commands that need the caller's user ID (like, repost, bookmark, follow, etc.) will auto-fetch it via `/2/users/me`. An auth failure there surfaces as an auth error.
|
||||
|
||||
---
|
||||
|
||||
## Agent Workflow
|
||||
|
||||
1. Verify prerequisites: `xurl --help` and `xurl auth status`.
|
||||
2. **Check default app has credentials.** Parse the `auth status` output. The default app is marked with `▸`. If the default app shows `oauth2: (none)` but another app has a valid oauth2 user, tell the user to run `xurl auth default <that-app>` to fix it. This is the most common setup mistake — the user added an app with a custom name but never set it as default, so xurl keeps trying the empty `default` profile.
|
||||
3. If auth is missing entirely, stop and direct the user to the "One-Time User Setup" section — do NOT attempt to register apps or pass secrets yourself.
|
||||
4. Start with a cheap read (`xurl whoami`, `xurl user @handle`, `xurl search ... -n 3`) to confirm reachability.
|
||||
5. Confirm the target post/user and the user's intent before any write action (post, reply, like, repost, DM, follow, block, delete).
|
||||
6. Use JSON output directly — every response is already structured.
|
||||
7. Never paste `~/.xurl` contents back into the conversation.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Auth errors after successful OAuth flow | Token saved to `default` app (no client-id/secret) instead of your named app | `xurl auth oauth2 --app my-app` then `xurl auth default my-app` |
|
||||
| `unauthorized_client` during OAuth | App type set to "Native App" in X dashboard | Change to "Web app, automated app or bot" in User Authentication Settings |
|
||||
| `UsernameNotFound` or 403 on `/2/users/me` right after OAuth | X not returning username reliably from `/2/users/me` | Re-run `xurl auth oauth2 --app my-app YOUR_USERNAME` (xurl v1.1.0+) to pass the handle explicitly |
|
||||
| 401 on every request | Token expired or wrong default app | Check `xurl auth status` — verify `▸` points to an app with oauth2 tokens |
|
||||
| `client-forbidden` / `client-not-enrolled` | X platform enrollment issue | Dashboard → Apps → Manage → Move to "Pay-per-use" package → Production environment |
|
||||
| `CreditsDepleted` | $0 balance on X API | Buy credits (min $5) in Developer Console → Billing |
|
||||
| `media processing failed` on image upload | Default category is `amplify_video` | Add `--category tweet_image --media-type image/png` |
|
||||
| Two "Client Secret" values in X dashboard | UI bug — first is actually Client ID | Confirm on the "Keys and tokens" page; ID ends in `MTpjaQ` |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- **Rate limits:** X enforces per-endpoint rate limits. A 429 means wait and retry. Write endpoints (post, reply, like, repost) have tighter limits than reads.
|
||||
- **Scopes:** OAuth 2.0 tokens use broad scopes. A 403 on a specific action usually means the token is missing a scope — have the user re-run `xurl auth oauth2`.
|
||||
- **Token refresh:** OAuth 2.0 tokens auto-refresh. Nothing to do.
|
||||
- **Multiple apps:** Each app has isolated credentials/tokens. Switch with `xurl auth default` or `--app`.
|
||||
- **Multiple accounts per app:** Select with `-u / --username`, or set a default with `xurl auth default APP USER`.
|
||||
- **Token storage:** `~/.xurl` is YAML. In Docker, use the Hermes subprocess HOME (`/opt/data/home` in the official image) so tokens land under `/opt/data/home/.xurl`. Never read or send this file to LLM context.
|
||||
- **Cost:** X API access is typically paid for meaningful usage. Many failures are plan/permission problems, not code problems.
|
||||
|
||||
---
|
||||
|
||||
## Attribution
|
||||
|
||||
- Upstream CLI: https://github.com/xdevplatform/xurl (X developer platform team, Chris Park et al.)
|
||||
- Upstream agent skill: https://github.com/openclaw/openclaw/blob/main/skills/xurl/SKILL.md
|
||||
- Hermes adaptation: reformatted for Hermes skill conventions; safety guardrails preserved verbatim.
|
||||
Reference in New Issue
Block a user