mcpField
P
pp-substack-reader

pp-substack-reader

github.com/mvanhorn生产力2.1k88 次安装今天更新
代码仓库

关于 pp-substack-reader

pp-substack-reader 是只读 CLI 技能,可将 Substack publication 归档到本地 SQLite 数据库,供离线搜索、全文阅读和分析,适合读者及 Agent 工作流。付费文章仅在用户具备访问权限时提供。

主要功能

  1. 01将 publication 归档至 SQLite
  2. 02离线搜索本地语料
  3. 03批量读取文章全文
  4. 04运行只读 SQL 分析
  5. 05生成跨 publication 摘要
  6. 06比较 publication 活跃情况

适用场景

  1. 01建立可搜索的 newsletter 归档
  2. 02离线查找多个 publication 的文章
  3. 03在 Agent 工作流中读取有权访问的付费文章
  4. 04分析文章发布频率与受众构成

MCP 相关

README 提及用于读取 Substack 的 MCP 服务;它按照与 CLI 相同的规则解析 session cookie。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mvanhorn/printing-press-library --skill pp-substack-reader

安装后在 Claude Code 中输入/pp-substack-reader确认已加载。

详细介绍

Substack Reader — Printing Press CLI

前置条件:安装 CLI

此技能依赖 substack-reader-pp-cli 二进制文件。**调用此技能中的任何命令前,必须先确认 CLI 已安装。**如果尚未安装,请先安装:

  1. 通过 Printing Press 安装程序安装。在 macOS/Linux 上,二进制文件默认安装到 $HOME/.local/bin;在 Windows 上,默认安装到 %LOCALAPPDATA%\Programs\PrintingPress\bin:
    npx -y @mvanhorn/printing-press-library install substack-reader --cli-only
    
  2. 验证:substack-reader-pp-cli --version
  3. 确保报告的安装目录位于将调用此技能的 Agent/运行时的 $PATH 中。

如果 npx 安装失败(没有 Node、无法联网等),请改用 Go 直接安装(需要 Go 1.26.6 或更高版本)。此方式会将文件安装到 $GOPATH/bin(默认为 $HOME/go/bin),因此请将该目录添加到 $PATH:

go install github.com/mvanhorn/printing-press-library/library/media-and-entertainment/substack-reader/cmd/substack-reader-pp-cli@latest

如果安装后 --version 报告“command not found”,说明运行时无法在 $PATH 中找到二进制文件所在目录。验证成功前,请勿继续运行此技能的命令。

Substack Reader 会将整个出版物归档到本地 SQLite 镜像中,供你搜索、执行 SQL 查询和离线阅读。免费文章无需登录;对于你有权解锁的付费文章,可使用自己的会话 Cookie 解锁——绝不转发,且始终由你自行选择是否使用。与其他所有 Substack 工具不同,它能构建不断累积的语料库,而不是每次调用时实时获取内容。

何时使用此 CLI

如果你想要一份可长期保存、可搜索的一个或多个 Substack 出版物本地副本,用于阅读、Agent 工作流或分析,尤其是想阅读某篇文章的全文,或离线搜索多份 newsletter,请使用 Substack Reader。如果你看重不断累积的语料库,而不是每次调用时实时获取内容,那么它就是合适的工具。

不适用场景

请勿在以下场景使用此 CLI:

  • 请勿用它发布、安排发布或管理你拥有的 Substack(它是只读的)——请使用 Substack 网页应用或发布工具。
  • 请勿用它批量抓取或转发你无权访问的付费内容——它只会按需读取你有权访问的内容。
  • 请勿用它管理你自己出版物的订阅者、付款或分析数据。

独特功能

其他任何用于此 API 的工具都不具备以下功能。

不断累积的本地语料库
  • archive — 将整个 Substack 出版物归档到本地 SQLite 镜像中,供你离线阅读、搜索和查询——没有其他 Substack 工具会构建持久语料库。--limit 0 会遍历整个存档;如果运行因达到 --limit 而停止,而非遍历完整个存档,输出会明确说明这一点(JSON 中包含 "exhausted": false)——切勿将因达到上限而停止的运行视为完整归档。

    使用此功能可将实时 newsletter 转化为持久、可查询的知识库,而不必每次都重新获取内容。

    substack-reader-pp-cli archive astralcodexten --limit 0
    
  • sql — 对本地 Substack 语料库执行只读 SQL,基于你已归档的数据进行各类分析——例如文章发布频率、读者群体构成和最长文章。

    如果要对已归档的内容进行临时分析,又不想重新获取内容或编写代码,请使用此功能。

    substack-reader-pp-cli sql "SELECT json_extract(data,'$.audience') AS audience, COUNT(*) FROM resources WHERE resource_type='posts' GROUP BY audience"
    
按访问权限读取
  • read — 一次调用即可读取一篇或多篇文章的全文;免费文章无需密钥,付费文章则可通过你自己的会话 Cookie 读取(前提是你已订阅),并会如实提示“仅提供预览,你无权访问”。读取多篇文章时,JSON 模式会输出一个封套数组(读取失败的文章会成为 {"post", "error"} 条目,其余文章仍会正常返回);只读取一篇文章时,仍会返回单个对象封套。

    使用此功能可将文章全文导入 Agent 工作流——一次调用即可读取多个 slug,无需编写 shell 循环,同时严格遵守用户的访问权限。

    substack-reader-pp-cli read astralcodexten/open-thread-441
    substack-reader-pp-cli read astralcodexten/slug-one astralcodexten/slug-two --agent --select slug,subtitle
    
主题与对比分析
  • digest — 汇总本地语料库中所有出版物在指定时间段内的新内容,并按排名一览无余。

    用作个人简报,了解“自上次同步以来,各份新闻通讯有哪些新内容”。

    substack-reader-pp-cli digest --since 7d
    
  • author-compare — 根据本地语料库,对比两份出版物的发布频率、主题以及免费与付费内容的比例。

    订阅前可用来评估一份新闻通讯,也可用来研究成功作者的发布内容。

    substack-reader-pp-cli author-compare astralcodexten blog.bytebytego.com
    

命令参考

categories — 浏览 Substack 的出版物类别

  • substack-reader-pp-cli categories browse — 列出某个类别中的出版物
  • substack-reader-pp-cli categories list — 列出所有 Substack 类别

publications — 发现 Substack 出版物

  • substack-reader-pp-cli publications <query> — 按名称搜索 Substack 出版物(尽力而为;匿名时可能返回的结果较少)
查找合适的命令

如果你知道想做什么,却不知道该用哪个命令,可以直接询问 CLI:

substack-reader-pp-cli which "<capability in your own words>"

which 会根据自然语言功能查询,从该 CLI 精选的功能索引中匹配最合适的命令。退出码 0 表示至少找到一个匹配项;退出码 2 表示没有可靠匹配项——请改用 --help,或缩小查询范围。

示例

构建可搜索的语料库
substack-reader-pp-cli archive astralcodexten --limit 0 && substack-reader-pp-cli search "prediction markets"

镜像整份出版物(--limit 0 = 不限数量),然后使用 FTS 排名进行离线搜索。精简版/Agent 搜索结果仅包含关键字段和长度受限的 snippet,不会返回完整文章正文;若要查看结果的完整文本,请使用 read(或 --select)。

提取长篇文章中的特定字段
substack-reader-pp-cli read astralcodexten/open-thread-441 --agent --select title,post_date,audience,body_html

仅提取 Agent 需要的字段,避免获取冗长文章对象中的所有内容。

一次调用读取多篇文章
substack-reader-pp-cli read astralcodexten/slug-one astralcodexten/slug-two astralcodexten/slug-three --agent --select slug,subtitle,audience

无需使用 Shell 循环即可批量读取一组 slug;JSON 模式会返回文章信封数组,即使某个 slug 无效,也不会影响其余结果。

受众构成分析
substack-reader-pp-cli sql "SELECT json_extract(data,'$.audience') AS audience, COUNT(*) FROM resources WHERE resource_type='posts' GROUP BY audience"

针对本地语料库执行只读 SQL,进行任意分析。

身份验证设置

免费和公开文章无需密钥,零配置。 安装 CLI 后,每条命令都能立即运行:无需账户、API 密钥,也无需配置任何内容。只有付费文章才需要身份验证。

有一个可选层:你自己的 Substack 会话 Cookie(substack.sid)。Substack 会根据你提供的会话在服务器端判断访问权限,因此,将你自己的 Cookie 提供给读取者后,它只能解锁你已订阅的付费文章,除此之外别无其他。它是你自己的 Cookie,绝不是 API 密钥;读取免费内容不需要它,并且一个 Cookie 就涵盖你账户上的所有订阅(访问权限按账户而非按出版物计算)。

可通过以下三种方式中的任意一种提供(以首先命中的方式为准):

# 1. Environment variable — the bare substack.sid value...
export SUBSTACK_SESSION="s%3A..."
# ...or the whole cookie fragment, if that is what you copied
export SUBSTACK_SESSION="substack.sid=s%3A..."

# 2. A JSON cookie file at the default config-dir path
#    (~/.config/substack-reader-pp-cli/cookie.json on the platform default).
#    The directory does not exist on a clean install — create it first.
mkdir -p ~/.config/substack-reader-pp-cli && chmod 700 ~/.config/substack-reader-pp-cli
echo '{"substack.sid":"s%3A..."}' > ~/.config/substack-reader-pp-cli/cookie.json
chmod 600 ~/.config/substack-reader-pp-cli/cookie.json

# 3. The same JSON cookie file, kept anywhere on disk
export SUBSTACK_COOKIE_FILE=/path/to/substack-cookie.json

优先级依次为 SUBSTACK_SESSION、SUBSTACK_COOKIE_FILE,然后是默认配置目录中的文件。设置 SUBSTACK_COOKIE_FILE 后,它具有权威性:如果其中的文件不存在、不可读、无法解析或不含 substack.sid,就会报错,绝不会静默回退到过期的默认值。默认配置目录中的文件则采用尽力而为策略:如果文件存在但无法读取或解析,会降级为匿名模式并发出警告;如果文件不存在,或者文件可以解析但不含 substack.sid,则会静默降级为匿名模式。可通过 SUBSTACK_READER_CONFIG_DIR 或 SUBSTACK_READER_HOME 更改配置目录的位置。

从浏览器中获取 Cookie。 打开 DevTools → Application → Cookies → https://substack.com,然后复制 substack.sid 的值。只需要 substack.sid,不需要 connect.sid。请直接复制,不要重新输入:只要把大写字母 I 误输为小写字母 l,Cookie 就会静默地被视为匿名状态。

使用 MCP 服务时。 MCP 服务解析会话的方式与 CLI 完全相同,因此配置目录中的 Cookie 文件完全不需要 env 块——保存 cookie.json 后,服务就会读取它。如果你更希望显式配置,可以在 Claude Desktop 配置的 env 中设置其中一个变量(MCPB bundle 会将其作为可选字段提示你填写)。

你的 Cookie 始终属于你。 除了发送给 Substack 本身,它不会离开你的设备;在所有输出路径中都会被遮蔽,因此脚本运行时不会泄露;也绝不会被转发。CLI 持久化保存的任何副本都会以 0600 权限写入,并位于权限为 0700 的父目录中;如果 Cookie 文件可被组或其他用户读取,则会发出警告。

失败会如实报告,绝不静默处理。 没有会话时,付费文章会返回 Substack 的公开预览,并明确说明:access: preview、authenticated: false、full: false,同时在 stderr 上输出 preview only (N of ~M words) — no session configured。访问权限降级始终会明确显示,绝不会伪装成完整读取。

运行 substack-reader-pp-cli doctor 验证设置。

Agent 模式

为任意命令添加 --agent。它会展开为:--json --compact --no-input --no-color --yes。

  • 可通过管道处理——JSON 输出到 stdout,错误输出到 stderr

  • 可筛选——--select 可保留部分字段。点分路径可深入嵌套结构;数组会逐元素遍历。对于内容冗长的 API,这对控制上下文大小至关重要:

    substack-reader-pp-cli categories list --agent --select id,name,status
    
  • 可预览——--dry-run 可显示请求而不发送

  • 支持离线使用——有本地 SQLite 存储时,同步/搜索命令可以使用它

  • 非交互式——绝不提示输入,所有输入都通过标志传入

  • 只读——请勿使用此 CLI 执行创建、更新、删除、发布、评论、点赞、邀请、下单、发送或其他会修改数据的请求

响应封装

从本地存储或 API 读取数据的命令会将输出封装在一个来源信息对象中:

{
  "meta": {"source": "live" | "local", "synced_at": "...", "reason": "..."},
  "results": <data>
}

解析 .results 获取数据,并查看 .meta.source 以判断数据来自实时 API 还是本地存储。只有当 stdout 连接到终端,且未设置机器格式标志(--json、--csv、--compact、--quiet、--plain、--select)时,才会将人类可读的 N results (live) 摘要输出到 stderr;通过管道读取的 Agent 和使用显式格式运行的命令,stdout 上只会有纯 JSON。

路径和状态

Agent 应将 CLI 的路径解析器视为运行时契约的一部分:

  • 使用 --home <dir> 设置单次调用的路径,或设置 SUBSTACK_READER_HOME=<dir>,将四种路径统一迁移到同一个根目录下。

  • 仅在特定类型的路径需要单独设置时,才使用对应的环境变量:SUBSTACK_READER_CONFIG_DIR、SUBSTACK_READER_DATA_DIR、SUBSTACK_READER_STATE_DIR、SUBSTACK_READER_CACHE_DIR。

  • 解析顺序为:对应类型的环境变量、--home、SUBSTACK_READER_HOME、XDG(XDG_CONFIG_HOME、XDG_DATA_HOME、XDG_STATE_HOME、XDG_CACHE_HOME),然后是平台默认值。

  • config 存放 config.toml 和配置档等设置。data 存放 credentials.toml、data.db、Cookie 和身份验证辅助文件。state 存放持久化查询、作业和 teach.log。cache 存放可重新生成的 HTTP/缓存文件。

  • 存储的密钥位于 data 目录下的 credentials.toml 中。为保持兼容性,仍会读取现有的旧版 config.toml 密钥,并在首次写入身份验证信息时将其移出 config.toml。

  • 运行 substack-reader-pp-cli doctor --fail-on warn,以显示路径和凭据位置警告。对于需要获取解析后目录路径的 Agent,agent-context 会提供 schema v4 的 paths 块。

  • 对于 MCP,请在 MCP 主机配置中传递路径迁移设置。MCP 二进制文件不会继承 CLI 标志:

    {
      "mcpServers": {
        "substack-reader": {
          "command": "substack-reader-pp-mcp",
          "env": {
            "SUBSTACK_READER_HOME": "/srv/substack-reader"
          }
        }
      }
    }
    

集群优先级:继承的对应类型环境变量会覆盖该类型的显式 --home。请使用 SUBSTACK_READER_HOME 或对应类型的环境变量作为持久的集群配置手段,仅将 --home 用于单次调用。取消设置环境变量无法还原路径迁移;清除 SUBSTACK_READER_HOME 前,请手动移动文件,否则 doctor 将无法找到留在原根目录下的凭据。

自动学习

此 CLI 内置了自动捕获的学习循环。CLI 会自行记录:每次调用都会在本地留档;某个标志失败后经修正重试,会自动推导出一个 flag_alias 候选项;对尚无操作手册的查询类别执行 teach,会自动根据会话日志综合生成一个 playbook_candidate。你只需做出判断:先运行 recall,处理显示的候选项,使用最终答案执行 teach,发现修正时运行 playbook amend。你无需手动记录失败情况。

步骤 1:任何探索操作前先运行 recall

针对新的用户问题,在运行列表、搜索或深入查询命令前,先运行:

substack-reader-pp-cli recall "<user's question>" --agent

响应封装:

{
  "query": "...",
  "normalized": "<normalized form>",
  "query_entities": ["..."],
  "found": true | false,
  "match_score": 0.0,
  "results": [
    { "resource_id": "...", "resource_type": "...", "venue": "...",
      "confidence": 2, "entity_match": "exact|partial|unknown",
      "source": "taught|preseed|pattern", "warnings": ["..."] }
  ],
  "mismatches": [ /* only when --debug-mismatches */ ],
  "warnings": [ /* top-level */ ],
  "candidates": [
    { "id": 12, "class": "flag_alias | playbook_candidate",
      "summary": "...", "sightings": 3, "last_seen": "...",
      "rationale": "...",
      "next_action": ["<trial command>", "substack-reader-pp-cli learnings confirm 12"] }
  ],
  "playbook": {
    "query_family": "...",
    "playbook": {
      "steps": [ { "cmd": "<command with {slot} substitution>", "purpose": "..." } ],
      "entity_slots": ["$ENTITY"],
      "expected_tool_calls": 3
    },
    "slots_resolved": { "$ENTITY": { "token": "<live token>", "canonical": "<canonical>" } },
    "notes": "<workarounds + gotchas for this query family>"
  },
  "notes": "<duplicate surface for non-playbook callers>"
}

空存储时的捷径:如果存储中还没有学习记录、操作手册或候选项(recall 没有结果,且 learnings list 和 learnings candidates 均为空),则本会话后续查询可跳过 recall,以免每次查询都增加负担;在记录了内容后,再恢复先运行 recall 的做法。

步骤 2:决策树

按以下顺序查看 candidates、playbook、notes、results[0] 和警告:

if Candidates present (warnings include "candidates_present"):
    -> candidates are try-then-confirm, never facts. Follow each candidate's
       two-step next_action verbatim: run the trial command first, then run
       `learnings confirm <id>` only after the trial verified the behavior.
       Reject a wrong candidate with `learnings reject <id>`.
    -> NEVER re-teach something recall surfaced as a candidate; confirm or
       reject that candidate instead of teaching a duplicate.
    -> candidates ride alongside playbooks and resource hits, not instead of
       them; continue with the branches below after acting on them.

if Playbook present:
    -> READ Playbook.notes verbatim FIRST (workarounds + gotchas the CLI surface doesn't expose)
    -> replay Playbook.steps in order, substituting Playbook.slots_resolved entries
       for the entity slot tokens. If a step's slot is unresolved, fall back to
       discovery for that step only.
    -> the Playbook's expected_tool_calls is a budget; if you find yourself running
       materially more, record the divergence via `substack-reader-pp-cli playbook amend`
       at end-of-session.

elif Notes present (no Playbook):
    -> read Notes verbatim before any discovery step; they carry known gotchas
       for this query family even when no structured choreography exists yet.

elif Found AND Results[0].EntityMatch == "exact" AND Results[0].Confidence >= 2:
    -> skip discovery; fetch live data for Results[*].ResourceID in parallel

elif Found AND Results[0].EntityMatch == "partial":
    -> candidate hint, NOT a hit; read the resource title to validate before trusting

elif (any row in Mismatches[] when --debug-mismatches was passed):
    -> treat as cold start; the stored learning is for a different entity
       (different canonical resolved from query_entities)

else:  // Found == false, no playbook, no notes
    -> cold start; run discovery normally; teach the answer afterward (Step 4).
       If the family has no playbook yet, that teach auto-synthesizes a
       playbook candidate from this session's journal - you do not need to
       record one by hand.

操作手册和 Notes 与每项资源的处理路径彼此独立。recall 响应可以同时包含操作手册和 Results[] 命中项——两者都要使用:操作手册说明要执行哪套操作流程;资源命中项则可跳过特定步骤。默认跳过 mismatches;仅在调查冷启动时出现的意外情况时,才传入 --debug-mismatches。

候选项判断详情:learnings confirm <id> 会在生成候选项前打印其完整内容——请确认打印的内容与已验证的行为相符。learnings reject <id> 会将推导签名标记为废弃,从而避免同一候选项再次出现。封装中只包含当前值得处理的少数候选项;substack-reader-pp-cli learnings candidates 会列出所有未处理的候选项。

平稳降级:如果 learnings confirm 是未知命令,说明你使用的是较旧版本的二进制文件——忽略候选项相关说明,并继续遵循其余流程。

第 3 步:始终读取 warnings
  • low_confidence:存在 confidence<2 的行。将其视为提示,而非跳过发现的命中项。
  • resource_not_in_store:本地存储中没有学习内容指向的资源。匹配验证器无法对实体进行分类——直接获取并重新评估。
  • cross_alias_match(针对每个结果):该行是在不同别名下教入的,并通过 entity_lookups 与实时查询的规范实体匹配(例如,针对“USA”的教入满足了“United States”的回忆)。信任 resource_id。
  • similar_shape_different_entity:<canonical>(顶层):存在结构匹配的行,但其规范实体与实时查询的不同。按冷启动处理;警告会将冲突的规范实体作为提示提供,但该行不会被提升到 Results 中。
  • ambiguous_alias(顶层):单个查询实体解析到了多个规范实体(例如,“Cards” → Arizona Cardinals + St. Louis Cardinals)。在确定资源前,先结合上下文说明这种歧义。
  • candidates_present(顶层):该 envelope 包含 candidates 部分。先按第 2 步中的候选项分支处理,再进行其他操作。
  • lookup_refresh_available(顶层):查询中的某个实体尚无 lookup 行,但同步数据可能会提供。运行 substack-reader-pp-cli sync 以刷新实体 lookup。
  • 顶层 no_learnings_for_query_family:表中没有高于 Jaccard 下限的行。属于纯冷启动。
第 4 步:完成回复后始终执行 teach &

教学是无条件的。解决存储无法回答的查询后,在后台教入最终资源映射——不设调用次数门槛,也不判断学习是否“值得”。教入是闭环的锚点:对于没有操作手册的类别,它会触发操作手册综合;指向同一对象的不同表述会归并到同一类别,因此近似重复的教入不会让存储碎片化。组装好面向用户的回复后、发出回复前,使用 shell & 执行,让调用立即返回:

substack-reader-pp-cli teach --query "<user's question>" --resource-type <type> --resource <id1> --resource <id2>
# (append shell `&` to background it)

成功时不输出任何内容。错误只会写入已解析状态目录中的 teach.log。教入最具体的资源——如果用户提出的是宽泛问题,而你通过逐级检查父记录找到了具体答案,就教入叶子 id,而不是父级。CLI 会在回忆时使用预置的 entity_lookups 解析跨别名,因此,在一个别名下教入(例如,“Niners”)会自动满足未来使用其他别名(例如,“49ers”、“San Francisco”)的查询。

PII 规则:教入结构性问题时,必须移除标识符——教入的查询或备注中绝不能包含姓名、电子邮件、电话号码、账户 id 或其他个人标识符。CLI 会扫描教入查询中明显的电子邮件/电话号码格式并发出警告,但不会阻止;请在教入前移除这些信息,不要依赖警告。

第 5 步:playbook——可选标志,自动合成

你无需判断某个会话是否“值得”整理成 playbook:对于尚无 playbook 的类别执行 teach 时,系统会根据会话日志自动合成一个 playbook_candidate,并由下一次会话通过 confirm/reject 来判定。只有当你已经掌握值得逐字记录的操作流程时,才附加明确的 playbook 标志——例如 CLI 未显示出来的变通方法(被静默丢弃的标志、未记录的参数、分页技巧、负载陷阱)。优先使用集成式单次调用形式——在同一次 teach 调用中记录资源学习和 playbook:

# Common case: record both the resource learning AND the playbook in one call.
substack-reader-pp-cli teach \
  --query "<user's question>" \
  --resource <id> \
  --playbook-file ~/playbooks/<shape>.json \
  --playbook-notes-file ~/playbooks/<shape>-notes.md
# (append shell `&` to background it)

# Alternate: playbook-only (no resource to record alongside).
substack-reader-pp-cli teach-playbook \
  --query "<user's question>" \
  --playbook-file ~/playbooks/<shape>.json \
  --notes-file ~/playbooks/<shape>-notes.md

Playbook 文件为 JSON 格式,包含 steps、entity_slots、expected_tool_calls。Notes 文件为 Markdown 格式,逐字记录易踩的坑。仅使用文件的调用方(仅使用 MCP 的 Agent)可直接以内联形式传入相同内容:在集成式 teach 形式中使用 --playbook-json 和 --playbook-notes,在 teach-playbook 中使用 --playbook-json 和 --notes。在集成式 teach 形式中,playbook 标志是可选的——如果只需教授资源,则完全省略这些标志。在独立的 teach-playbook 形式中,必须至少设置 playbook 或 notes 标志之一;两者都为空时会被拒绝。Playbook 按结构化查询类别(去除实体后的查询)编目,因此,从一种实体形状的查询中教授的操作流程适用于所有其他形状相同的查询;调用时由 slots_resolved 绑定当前查询的规范形式。

如果你在未来的调用中找到 playbook,请将其视为事实依据:使用 slots_resolved 替换值,按步骤重放,跳过操作流程中已记录的探索,并在执行任何步骤前阅读 notes。

第 6 步:调试响应指出修正内容时,使用 playbook amend &

如果你的调试协议响应指出了 notes 或 playbook 应该记录的具体修正——例如变通方法、未记录的端点结构、过时的字段名称、观察到的架构漂移、空负载回退方案——请在生成面向用户的响应之前触发 playbook amend。其调用方式与 teach 相同,均为触发后无需等待结果。

substack-reader-pp-cli playbook amend \
  --query "<exact recall query string>" \
  --add-note "<your concrete correction>"
# (append shell `&` to background it)

哪些内容值得修订:本次会话中你观察到、未来的你也会受益的行为。值得修订的示例:

  • CLI 界面会静默丢弃或改变标志顺序时的变通方法。
  • 未记录的端点结构(响应封装在 {meta, results} 中,负载嵌套层级比文档所述多两层)。
  • 观察到的架构漂移(字段重命名、不同赛季间索引变化、API 现在返回小写类别标签)。

以下内容不应写入 notes:

  • 用户问题中与年份或实体相关的答案。这是响应内容,不是学习所得。
  • Playbook 已在运行时检索的、按团队/运动员/行数据区分的信息。
  • 对现有 notes 内容的改述。

amend 命令会将内容附加到该类别现有的 notes 中,并添加带时间戳的标记([amend YYYY-MM-DDTHH:MMZ]: <text>)。多次 amend 会累积,且可查看审计记录。如果该类别尚无 playbook,amend 会创建一个仅含 notes 的 playbook(因此冷启动时的修正也能记录下来)。

amend notes 中的个人身份信息规范

playbook amend notes 未来可能会作为共享知识流入 Printing Press 的后续版本。请勿在其中包含可识别用户身份的信息,以便上游贡献路径保持畅通,无需事后清理:

  • 请勿嵌入用户文件系统路径、个人 API 密钥或令牌、用户电子邮件地址、用户 GitHub 用户名,或与单个用户关联的特定查询历史。
  • 可以包含:端点结构、未记录的字段名称、API 易踩的坑、观察到的架构漂移、CLI 界面的变通方法、可推广的分页或重试策略。

如果某项修正只有结合用户特定的上下文才有意义,应将其记录在个人笔记中,而不是 playbook amend 中。

衡量闭环效果

substack-reader-pp-cli learnings stats 会根据本地 learn_events 表报告召回命中率、教学复用率、剧本解决率,以及候选项的确认/拒绝次数。分母尚不存在时,相关比率为 null;所有数据都保留在本机。使用此命令检查该闭环是否为此 CLI 带来了价值。

禁用学习
  • 在单条命令上使用 --no-learn,即可同时短路 recall 和 teach 写入路径。适用于确定性 Agent 工作流,或不得受到累积学习影响的测试。
  • 在环境中设置 SUBSTACK_READER_NO_LEARN=true,即可全局禁用该管道。

Agent 反馈

当你(或 Agent)发现此 CLI 有不对劲的地方时,请记录下来:

substack-reader-pp-cli feedback "the --since flag is inclusive but docs say exclusive"
substack-reader-pp-cli feedback --stdin < notes.txt
substack-reader-pp-cli feedback list --json --limit 10

条目会以 feedback.jsonl 的形式存储在解析后的数据目录中。除非设置了 SUBSTACK_READER_FEEDBACK_ENDPOINT,并且传入了 --send 或设置了 SUBSTACK_READER_FEEDBACK_AUTO_SEND=true,否则不会发送 POST 请求。默认行为仅在本地存储。

写下让你感到意外的地方,而不是提交 Bug 报告。简短、具体、写成一行:这才是会不断累积价值的内容。

输出交付

每条命令都接受 --deliver <sink>。输出会发送到指定目标,同时输出到 stdout,或取代 stdout 输出,以便 Agent 无需手动管道传输即可路由命令结果。支持三种目标:

目标效果
stdout默认;仅写入 stdout
file:<path>将输出原子写入 <path>(tmp + rename)
webhook:<url>将输出正文 POST 到该 URL(使用 --compact 时,内容类型为 application/json 或 application/x-ndjson)

不支持的方案会被拒绝,并返回指出受支持方案的结构化错误。Webhook 请求失败时会返回非零状态码,并在 stderr 中记录 URL 和 HTTP 状态码。

命名配置

配置是已保存的一组标志值,可在多次调用中重复使用。适用于定时或周期性运行、每次输入不同但重复使用相同已保存标志的 Agent。

substack-reader-pp-cli profile save briefing --json
substack-reader-pp-cli --profile briefing categories list
substack-reader-pp-cli profile list --json
substack-reader-pp-cli profile show briefing
substack-reader-pp-cli profile delete briefing --yes

显式标志始终优先于配置值;配置值优先于默认值。agent-context 会在 available_profiles 下列出所有可用配置,便于执行运行时自检的 Agent 发现它们。

退出代码

代码含义
0成功
2用法错误(参数不正确)
3未找到资源
5API 错误(上游问题)
7触发速率限制(等待后重试)
10配置错误

参数解析

解析 $ARGUMENTS:

  1. 为空、help 或 --help → 显示 substack-reader-pp-cli --help 输出
  2. 以 install 开头 → 以 mcp 结尾 → 安装 MCP;否则 → 参见上文的先决条件
  3. 其他任何内容 → 直接使用(使用 --agent 将其作为 CLI 命令执行)

安装 MCP 服务

  1. 安装 MCP 服务:
    go install github.com/mvanhorn/printing-press-library/library/media-and-entertainment/substack-reader/cmd/substack-reader-pp-mcp@latest
    
  2. 在 Claude Code 中注册:
    claude mcp add substack-reader-pp-mcp -- substack-reader-pp-mcp
    
  3. 验证:claude mcp list

直接使用

  1. 检查是否已安装:which substack-reader-pp-cli 如果未找到,请提出安装建议(参见本技能开头的先决条件)。
  2. 根据用户查询,从上文的独特功能和命令参考中匹配最合适的命令。
  3. 使用 --agent 标志执行:
    substack-reader-pp-cli <command> [subcommand] [args] --agent
    
  4. 如果存在歧义,请查看子命令帮助:substack-reader-pp-cli <command> --help。

评论

支持 Markdown