supsub-ai/supsub-cli

supsub-sub

SupSub 订? / ? X」「X ? / 订?

First seen Jun 29, 2026

Installation

$ npx skills add supsub-ai/supsub-cli --skill supsub-sub

Summary

SupSub 订阅管理 —— 列出 / 添加 / 删除订阅源(微信公众号 MP、网站 WEBSITE、推特 X),浏览某个已订阅源里的文章列表(支持翻页),以及把某个源整源标记为已读。匹配「列出我的订阅」「我订阅了哪些」「取消订阅某个号」「订阅某个公众号 /…

Also in this package

Other skills from supsub-ai/supsub-cli.

npx skills add supsub-ai/supsub-cli

Browse all from supsub-ai/supsub-cli

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 5
Default branch master
Open issues 0
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Version0.1.5

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 15,702 B
  • docs SUMMARY.md 1,698 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 51 installs

SKILL.md

supsub-sub Skill

List, add, remove subscription sources, and browse the articles inside each source. Sources are typed MP(微信公众号)、WEBSITE(网站)或 X(推特 / Twitter 平台账号)。

⚠️ 关于字母「X」的歧义(务必先读)

本文档里 X 有两种完全不同的含义,不要混淆:

  1. --type X —— 这是一个真实的 sourceType 枚举值,专指 推特 / Twitter / X 平台账号。
  2. 占位符 X —— 用户口语里说「订阅 X」「取消订阅 X」「X 公众号最近的文章」时,这里的 X 通常只是一个占位代号,代指某个具体账号(可能是公众号、网站,也可能就是名字里带 X),与推特无关。

判定规则:

  • 只有当用户明确提到「推特 / Twitter / X 平台」时,才使用 --type X。
  • 用户说「订阅 X」「X 公众号」「看 X 的文章」而没有提到推特平台时,不要据此推断 --type X;这里的 X 是占位符,应按其真实类型(多为 MP 或 WEBSITE)处理。

Prerequisites

  • 安装:curl -fsSL https://raw.githubusercontent.com/SupSub-AI/supsub-cli/master/scripts/install.sh | bash(native 安装,装到 ~/.local、支持后台自动更新);或包管理器 npm i -g @supsub/cli / pnpm add -g @supsub/cli
  • 已登录:supsub auth status 显示 Authenticated(首次使用先 supsub auth login)
  • 未授权(exit 2 / UNAUTHORIZED)时不要止步于「你未登录」:直接运行 supsub auth login 为用户打开浏览器授权(命令会自动打开浏览器并阻塞等待授权,请用足够长的超时,如 10 分钟;用户只需在浏览器点确认,无需在终端输入任何内容),授权成功后重试原命令。无浏览器 / 无头环境再回退为提示用户 SUPSUBNOBROWSER=1 supsub auth login。

Commands

List subscriptions

supsub sub list [--type <MP|WEBSITE|X>]
Flag Default Description
--type — (all) 过滤来源类型:MP(公众号)/ WEBSITE(网站)/ X(推特)

Each row includes: sourceId, sourceType, name, img, description, unreadCount。表格模式下 类型 列会把 sourceType 显示为中文:MP→公众号、WEBSITE→网站、X→推特。

# 列出全部订阅
supsub sub list

# 仅看公众号
supsub sub list --type MP

# 仅看网站
supsub sub list --type WEBSITE

# 仅看推特(X 平台账号)
supsub sub list --type X

# 导出 JSON
supsub sub list -o json

JSON shape: {"success":true,"data":[{"sourceType":"MP","sourceId":12345,"name":"...","img":"...","description":"...","unreadCount":3}, ...]}


Add a subscription

sub add 有两条互斥入口,对应两种"拿到的 ID 形态":

# A) 全局搜索 / 已收录源 → 用内部正整数 sourceId
supsub sub add --source-id <id> --type <MP|WEBSITE|X> [--group <gid>]...

# B) mp search 发现的微信原生公众号 → 用 base64 字符串 mpId
supsub sub add --mp-id <mpId> [--type MP] [--group <gid>]...
Flag Required Description
--source-id 二选一 信息源 ID(正整数)。来自 supsub search / supsub sub list 的 sourceId
--mp-id 二选一 公众号 mpId(base64 字符串)。来自 supsub mp search 返回结果
--type 见说明 --source-id 模式必填,取 MP / WEBSITE / X(推特);--mp-id 模式可省,传了必须是 MP
--group no 分组 ID(数字,可重复指定多个)

互斥:--source-id 与 --mp-id 必须恰好二选一。同时给 / 都不给都会抛 INVALID_ARGS。

两条路径走的是不同 endpoint:--source-id → POST /api/subscriptions(已收录源订阅);--mp-id → POST /api/mps(按微信原生 ID 把新公众号纳入并订阅)。

# 路径 A:sub list / search 看到的内部 sourceId(公众号)
supsub sub add --source-id 12345 --type MP

# 路径 A:网站 + 多分组
supsub sub add --source-id 67890 --type WEBSITE --group 1 --group 2

# 路径 A:推特账号(用户明确说要订阅某个 Twitter / X 平台账号时)
supsub sub add --source-id 24680 --type X

# 路径 B:mp search 拿到的 mpId(base64)
supsub sub add --mp-id "MzkyNTYzODk0NQ=="

# 路径 B + 分组
supsub sub add --mp-id "MzkyNTYzODk0NQ==" --group 3

# JSON 输出
supsub sub add --source-id 12345 --type MP -o json

JSON shape: {"success":true,"data":{"message":"..."}}

--source-id 必须是正整数(非数字 → INVALIDARGS);--mp-id 是字符串,不做格式校验,由后端裁决;--group 接受多个数字 ID,非数字会报 INVALIDARGS (exit 64)。


Remove a subscription

supsub sub remove --source-id <id> --type <MP|WEBSITE|X>
Flag Required Description
--source-id yes 信息源 ID(正整数)
--type yes MP(公众号)/ WEBSITE(网站)/ X(推特)

⚠️ 退订是破坏性操作,执行前必须向用户二次确认:复述源名称(从 sub list 取),获得明确同意后才执行。
与 mark-read 一样,CLI 不会再问一次——把命令交给用户自己在终端跑时,不要说「执行后会提示确认」。

⚠️ 退订会连带把该源移出它所在的全部分组(实测:订阅并入组后退订,group subs 返回空)。
分组本身不受影响。重新订阅不会自动恢复原来的分组归属,需要再跑一次 group add-sub。
因此用户说「先退订,回头再订回来」时,要提醒他分组归属会丢。

# 取消订阅某个公众号(需用户确认后执行)
supsub sub remove --source-id 12345 --type MP

# 取消订阅某个网站
supsub sub remove --source-id 67890 --type WEBSITE -o json

# 取消订阅某个推特账号
supsub sub remove --source-id 24680 --type X

JSON shape: {"success":true,"data":{"message":"..."}}


Browse articles in a subscription

supsub sub contents --source-id <id> --type <MP|WEBSITE|X> [--unread | --all] [--brief] [--page <n>] [--page-size <n>]
Flag Default Description
--source-id required 信息源 ID(正整数)。别名 --id(与 focus contents --id 对齐,两个写法等价)
--type required MP(公众号)/ WEBSITE(网站)/ X(推特)
--unread (default) 仅返回未读文章
--all — 返回全部文章(已读 + 未读)
--brief — 精简输出:只留 contentId / title / publishedAt(+Text) / isRead
--page 1 页码(正整数)
--page-size 20 每页条数,1-100

⚠️ --unread 与 --all 互斥:同时指定会抛 INVALID_ARGS。不传任何一个时,默认行为等价于 --unread。

⚠️ 「最近」不等于「未读」—— 按用户意图选 flag,别让默认值替用户做决定:

用户说法 意图 用哪个
「看一下最近 X 的内容」「X 最近有什么」「X 讲了什么」「X 更新了吗」 按时间看这个源在讲什么 --all,isRead 只用来标注(「其中 3 篇你还没看过」),不要拿来过滤
「X 还有什么没看的」「X 的未读」「X 剩几篇没读」 明确要未读 默认(--unread)
说不清 —— --all + 标注哪些未读

用默认值应付「看一下最近 X 的内容」会踩这个坑:该源已经读完时返回空数组,于是回复「没有内容」—— 可用户问的是「最近」,不是「没读的」。只有用户话里出现「未读 / 没看的 / 没读的 / 清未读」才该过滤。

批量枚举一律加 --brief:默认输出每条都带 summary / coverImage / tags,100 条约 90KB, 足以撑爆工具输出上限被迫落盘;--brief 同样 100 条只有约 14KB。只在需要给用户讲内容时才用完整输出。

每行字段:contentId, url, title, coverImage, tags[], summary, publishedAt, publishedAtText, isRead。 --brief 时只有 contentId, title, publishedAt, publishedAtText, isRead。

publishedAt 是 Unix 秒级时间戳(少数后端版本为字符串),**publishedAtText 是 CLI 已格式化好的
"YYYY-MM-DD HH:mm",直接用即可,不要自己写 date -r / 时间戳转换**。排序仍用 publishedAt。

# 默认(未读)—— 看某个公众号里有哪些文章
supsub sub contents --source-id 12345 --type MP

# 全部文章(含已读)
supsub sub contents --source-id 12345 --type MP --all

# 看更早的文章:翻到第 2 页
supsub sub contents --source-id 12345 --type MP --page 2

# 一次多拿一些(上限 100)
supsub sub contents --source-id 12345 --type MP --page-size 100 -o json

# 全部文章 + JSON
supsub sub contents --source-id 12345 --type MP --all -o json

JSON shape: {"success":true,"data":[{"contentId":"...","url":"...","title":"...","coverImage":"...","tags":[...],"summary":"...","publishedAt":<timestamp 或字符串>,"isRead":false}, ...]}

publishedAt 的原始形态随后端版本而异(Unix 秒数字 / "YYYY-MM-DD HH:mm:ss" 字符串),
不用自己兼容——直接读 CLI 补好的 publishedAtText(恒为 "YYYY-MM-DD HH:mm")。

翻页策略:浏览类请求只拉第 1 页并告知总量;「全部 / 统计 / 导出」类请求用 --page-size 100 自动循环翻页(返回条数 < page-size 即末页),不要逐页询问用户。详见 supsub-unread。


Mark a source as read(整源标记已读)

supsub sub mark-read --source-id <id> --type <MP|WEBSITE|X> --all
Flag Required Description
--source-id yes 信息源 ID(正整数)
--type yes MP / WEBSITE / X(推特)
--all yes 确认整源全部标为已读(不可逆)

⚠️ 只支持整源已读,没有单篇已读。 文章没有「详情 / 阅读」这一步,产生不了自然的单篇已读事件,所以 CLI 只保留用户明确表达的「这个号我读完了」。传 --content-id 会抛 INVALID_ARGS。用户说「把这篇标为已读」时如实说明不支持,并问是否要整源已读。

⚠️ --all 必填且不可逆(CLI 无 mark-as-unread)。执行前必须向用户二次确认:复述源名称 + 当前未读数(从 sub list / supsub unread 取),获得明确同意后才执行。

CLI 不会再问一次:这条命令没有交互式确认提示,一执行就生效。若把命令交给用户自己在终端跑,
不要说「执行后 CLI 会提示确认」——那是不存在的安全网。

# 整源已读(需用户确认后执行)
supsub sub mark-read --source-id 12345 --type MP --all -o json

JSON shape: {"success":true,"data":{"message":"已标记该订阅源全部内容为已读(不可逆)"}}


Agent Usage Notes

  • 解析数据时统一用 -o json;表格输出有截断、列宽限制,不适合做下游处理。
  • 所有 JSON 响应都是 {"success":true,"data":<payload>} 结构(来自 src/ui/output.ts)。
  • --type 取值是 MP / WEBSITE / X(CLI 内部会 toUpperCase,大小写不敏感);常见错误是写成 mpaccount / wechat / rss / TWITTER 会报 INVALIDARGS——推特只接受 X,不接受 TWITTER。
  • 再次强调字母「X」的歧义:--type X = 推特平台账号。用户口语里「订阅 X」「X 公众号」「看 X 的文章」中的 X 多半是占位符代指某个具体账号,不要因为出现字母 X 就传 --type X;只有用户明确提到「推特 / Twitter / X 平台」时才用 --type X。
  • 添加订阅前先想清楚 ID 来源:

- 来自 supsub search / sub list 的 内部正整数 sourceId → sub add --source-id <id> --type ... - 来自 supsub mp search 的 base64 字符串 mpId → sub add --mp-id <mpId>(type 默认 MP,可省) - 不要把 mp search 的 mpId 强转成数字塞进 --source-id —— 那是不同 ID 空间,会被后端拒。

  • sub contents 默认只看未读 —— 「看一下最近 X 的内容」「这个号有哪些文章」「X 讲了什么」这类按时间浏览的请求一律加 --all,把 isRead 当标注而不是过滤条件;只有用户明说「未读 / 没看的」才用默认。
  • sub contents 默认每页 20 条,用 --page / --page-size(上限 100)翻页取更早的历史。
  • 标记已读只有整源一档(mark-read --all):没有单篇已读,因为文章没有「详情 / 阅读」这一步。整源已读不可逆,执行前必须向用户二次确认。跨源的未读概览与清未读工作流见 supsub-unread。
  • 分组管理(把这个源加进某个分组、分组里有哪些订阅)见 supsub-group;订阅时可直接用 sub add --group <gid> 一步入组。
  • 重复订阅不是错误:实测对同一个源再跑一次 sub add 返回 {"success":true,"data":{"message":"订阅成功"}},exit 0(幂等)。不要据此判断"是否已订阅"——要判断请看 sub list 或搜索结果里的 isSubscribed。重复退订才会报错(订阅不存在,400 → exit 1),两者不对称。
  • Exit codes:0 OK,2 UNAUTHORIZED,64 INVALID_ARGS(--type / --source-id / --group / --page / --page-size 校验失败、--all 与 --unread 互斥、mark-read 未给 --all 或误传 --content-id),1 业务错误(后端 4xx,如源不存在 / 未订阅 / 重复退订),10 网络错误,11 服务端错误(5xx)。