| name | substack-sub |
| description | Substack 文章发现 / 单 URL ephemeral source skill。当前仅支持 `--url <substack-article-url>` 模式(与 medium-sub `--url` 完全对称的合成 JSON 直通);批量 discovery(按 publication archive / RSS feed 抓候选)是 Phase B,未实现。Trigger 一般不来自用户直接调用,而是被 material-pipeline 的 run.js 编排:当传入的 URL 是 Substack 域时由 orchestrator 路由到本 skill。Pure fetcher — 输出到 stdout 的单个 JSON 对象,不写文件、不应用 limit、不做跨日去重(那是 orchestrator 的事)。 |
| version | 1.0.0 |
Substack 文章订阅 / 候选发现器(pure fetcher)
和 medium-sub 是对称的姐妹 skill:medium-sub 服务 Medium,substack-sub 服务 Substack。两者输出完全相同的 JSON schema,让上层编排器(material-pipeline/run.js)的 Stage 2(dedup / per-tag limit / sub-list merge)保持 source-agnostic。
范围(v1.0.0:Phase A)
| 模式 | 状态 | 说明 |
|---|
--url <substack-article-url> | ✅ 已实现 | 单 URL ephemeral source;合成 JSON 直通,不抓任何 Substack 服务器;和 medium-sub --url 模式逐字对齐 |
--publications <hosts> | 🚧 Phase B | 按 publication archive 页抓"最新文章"列表,待 substack 日更需求出现时再做 |
--feeds <urls> | 🚧 Phase B | RSS feed 抓最新;与 publications 模式择一或并存 |
如果未来 Phase B 落地,Phase A 的 --url 模式不会被破坏。
为什么单独有这个 skill
理论上 --url 模式只是"把传入 URL 包成统一 JSON",与 hostname 无关——medium-sub --url 也能这么干(实测可以喂 substack URL 直接通过)。
但调用 medium-sub 来处理 substack URL 在语义上很奇怪。把它独立成 substack-sub 解决两个问题:
- 命名诚实:orchestrator 日志打印
调用 substack-sub --url X,读起来与抓 Substack 的语义一致
- 预留 slot:Phase B 加 Substack discovery 时已经有对应的 skill 目录、对应的注册表项,不必再重命名或拆分
适用场景
- 由 material-pipeline 的
run.js --url <X> 触发,且 X 落在 Substack 路由规则下:
- hostname 以
.substack.com 结尾
- 路径形如
/p/<slug>(覆盖自定义域,如 blog.dailydoseofds.com/p/...)
用户几乎不直接调用本 skill;由 orchestrator 按 URL 路由。
不适用
- 单篇文章下载 → 用
substack-fetch(leaf executor)
- Medium 的 tag recommended 列表 → 用 medium-sub
- 任何会员墙 / 付费内容发现:本 skill 不登录 substack、不带 cookie,只做 URL 直通
输出契约
stdout:单个 JSON 对象。与 medium-sub 完全相同的 schema:
{
"fetchedAt": "2026-05-27",
"generator": "substack-sub@1.0.0",
"source": "manual-url",
"stats": {
"totalArticles": 1,
"uniqueArticles": 1,
"tagsFetched": 0,
"tagsFailed": 0
},
"groups": [
{
"tag": "manual",
"articles": [
{
"title": "",
"url": "https://blog.dailydoseofds.com/p/the-anatomy-of-an-agent-harness",
"publishedAt": "2026-05-27",
"author": ""
}
]
}
]
}
stderr:暂无输出(--url 模式不需要进度日志;Phase B discovery 模式会在 stderr 打印 per-publication 进度)。
CLI
node fetch-list.js --url https://blog.dailydoseofds.com/p/the-anatomy-of-an-agent-harness
node fetch-list.js --url https://avichawla.substack.com/p/the-anatomy-of-an-agent-harness
node fetch-list.js --publications blog.dailydoseofds.com,avichawla.substack.com
node fetch-list.js --feeds https://blog.dailydoseofds.com/feed
与其他 skill 的关系
medium-sub substack-sub (本 skill)
────────── ───────────────────
discovery --tags ai,llm (Phase B)
──>抓 medium tag 列表 ──>未来抓 substack publication archive
──────────────── ───────────────────
--url 直通 ✅ 已实现 ✅ 已实现(v1.0.0 唯一模式)
──────────────── ───────────────────
输出 JSON 完全相同 schema 完全相同 schema
──────────────── ───────────────────
被谁调用 material-pipeline run.js material-pipeline run.js
按 URL 路由 按 URL 路由
下游一致:调用方拿到 stdout JSON → Stage 2 dedup / limit → Stage 3 按 URL 选 fetcher(substack URL → substack-fetch)→ 翻译 → 发布。
这个 skill 不做的事
- 不下载正文 → 用
substack-fetch
- 不写文件 → stdout JSON,调用方决定要不要持久化
- 不做跨日去重 / 不应用 limit → orchestrator (
run.js Stage 2) 的事
- 不验证 URL 真的是 Substack → orchestrator 通过
detectSource() 在调用前已经决定了路由;本 skill 信任传入
文件清单
substack-sub/
├── SKILL.md # 本文档
└── scripts/
├── package.json # type=module;v1.0.0 暂无 dep
├── fetch-list.js # 主脚本(--url 模式:~60 行)
└── .gitignore # 忽略 node_modules / package-lock.json