Summary
SupSub 订阅管理 —— 列出 / 添加 / 删除订阅源(微信公众号 MP、网站 WEBSITE、推特 X),浏览某个已订阅源里的文章列表(支持翻页),以及把某个源整源标记为已读。匹配「列出我的订阅」「我订阅了哪些」「取消订阅某个号」「订阅某个公众号 /…
supsub-ai/supsub-cli
SupSub 订? / ? X」「X ? / 订?
npx skills add supsub-ai/supsub-cli --skill supsub-sub
SupSub 订阅管理 —— 列出 / 添加 / 删除订阅源(微信公众号 MP、网站 WEBSITE、推特 X),浏览某个已订阅源里的文章列表(支持翻页),以及把某个源整源标记为已读。匹配「列出我的订阅」「我订阅了哪些」「取消订阅某个号」「订阅某个公众号 /…
Other skills from supsub-ai/supsub-cli.
npx skills add supsub-ai/supsub-cli
Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.
master
Parsed from SKILL.md frontmatter.
Files included with this skill beyond the listing page.
SKILL.md
15,702 B
SUMMARY.md
1,698 B
List, add, remove subscription sources, and browse the articles inside each source. Sources are typed MP(微信公众号)、WEBSITE(网站)或 X(推特 / Twitter 平台账号)。
本文档里 X 有两种完全不同的含义,不要混淆:
--type X —— 这是一个真实的 sourceType 枚举值,专指 推特 / Twitter / X 平台账号。判定规则:
--type X。--type X;这里的 X 是占位符,应按其真实类型(多为 MP 或 WEBSITE)处理。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/clisupsub auth status 显示 Authenticated(首次使用先 supsub auth login)supsub auth login 为用户打开浏览器授权(命令会自动打开浏览器并阻塞等待授权,请用足够长的超时,如 10 分钟;用户只需在浏览器点确认,无需在终端输入任何内容),授权成功后重试原命令。无浏览器 / 无头环境再回退为提示用户 SUPSUBNOBROWSER=1 supsub auth login。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}, ...]}
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)。
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":"..."}}
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。
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":"已标记该订阅源全部内容为已读(不可逆)"}}
-o json;表格输出有截断、列宽限制,不适合做下游处理。{"success":true,"data":<payload>} 结构(来自 src/ui/output.ts)。--type 取值是 MP / WEBSITE / X(CLI 内部会 toUpperCase,大小写不敏感);常见错误是写成 mpaccount / wechat / rss / TWITTER 会报 INVALIDARGS——推特只接受 X,不接受 TWITTER。--type X = 推特平台账号。用户口语里「订阅 X」「X 公众号」「看 X 的文章」中的 X 多半是占位符代指某个具体账号,不要因为出现字母 X 就传 --type X;只有用户明确提到「推特 / Twitter / X 平台」时才用 --type X。- 来自 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),两者不对称。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)。