mcpField
P
pp-substack-reader

pp-substack-reader

github.com/mvanhorn生産性2.1k88 回インストール今日更新
リポジトリ

pp-substack-reader について

pp-substack-readerは、SubstackのpublicationをローカルのSQLiteコーパスに保存する読み取り専用CLIスキルです。オフライン検索、全文閲覧、分析に対応し、読者やエージェントのワークフロー向けです。購読者限定記事は、ユーザーに閲覧権限がある場合のみ取得できます。

主な機能

  1. 01publicationをSQLiteに保存
  2. 02ローカルコーパスのオフライン検索
  3. 03複数記事の全文読み込み
  4. 04読み取り専用SQL分析
  5. 05複数publicationのダイジェスト
  6. 06publicationの活動状況比較

活用シーン

  1. 01検索可能なニュースレターアーカイブの作成
  2. 02複数publicationの記事をオフラインで検索
  3. 03エージェントのワークフローで購読者限定記事を読む
  4. 04記事の投稿頻度と読者構成の分析

MCP 対応

READMEではSubstackを読むためのMCPサーバーについて説明されています。CLIと同じルールでセッション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. インストール先ディレクトリが、このスキルを呼び出すエージェント/ランタイムの $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 クエリができ、オフラインでも読めます。無料投稿はログイン不要です。有料投稿は、自分が購読しているものを自分のセッションクッキーでロック解除できます。クッキーが再配布されることはなく、利用は常にオプトインです。他の Substack ツールとは異なり、呼び出しごとに最新データを取得するのではなく、使うほど充実するコーパスを構築します。

この CLI を使用する場面

1 つ以上の Substack 出版物を、読書、エージェントのワークフロー、分析に使うために、長期保存できて検索可能なローカルコピーが必要な場合は、Substack Reader を使用してください。特に、特定の投稿の全文を読んだり、ニュースレター全体をオフラインで検索したりする場合に役立ちます。呼び出しごとの最新データ取得よりも、使うほど充実するコーパスを重視する場合に適したツールです。

使用しない場面

次の目的には、この CLI を使用しないでください。

  • 自分が所有する Substack の投稿、公開スケジュール、管理には使用しないでください(読み取り専用です)。Substack のウェブアプリまたは公開ツールを使用してください。
  • 利用する権利のない有料コンテンツの一括スクレイピングや再配布には使用しないでください。取得できるのは、自分が利用する権利を持つコンテンツのみで、必要に応じて取得します。
  • 自分の出版物の購読者、支払い、分析データの管理には使用しないでください。

独自の機能

これらの機能は、この API の他のツールでは利用できません。

使うほど充実するローカルコーパス
  • archive — Substack 出版物全体をローカルの SQLite ミラーにアーカイブします。ミラーはオフラインで読んだり、検索やクエリを実行したりできます。永続的なコーパスを構築できる Substack ツールは他にありません。--limit 0 はアーカイブ全体を走査します。アーカイブを最後まで処理せず --limit で実行が停止した場合、出力にその旨が示されます(JSON では "exhausted": false が含まれます)。上限に達して停止した実行を、完全なアーカイブとして扱わないでください。

    最新のニュースレターを毎回取得するのではなく、長期保存できてクエリ可能なナレッジベースにしたい場合に使用してください。

    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 — 1 回の呼び出しで 1 件以上の投稿の全文を読み込みます。無料投稿はキー不要で、有料投稿は自分のセッションクッキーを使い、自分が購読しているものを読み込めます。利用権限がない場合は、「プレビューのみ。利用権限がありません」というメッセージが正直に示されます。複数の投稿を指定した場合、JSON モードではエンベロープの配列が出力されます(読み込みに失敗した投稿は {"post", "error"} エントリーとなり、他の投稿は引き続き返されます)。投稿が 1 件の場合は、単一オブジェクトのエンベロープが返されます。

    シェルのループを使わず、1 回の呼び出しで複数のスラッグを指定して投稿の全文をエージェントのワークフローに取り込み、ユーザーの利用権限の範囲を厳守したい場合に使用してください。

    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 — ローカルコーパスをもとに、2つの出版物の発行頻度、トピック、無料・有料コンテンツの割合を比較します。

    ニュースレターを購読する前に内容を把握したり、成功している著者の発信内容を調べたりするのに使えます。

    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 は少なくとも1つ一致したことを意味します。終了コード 2 は確度の高い一致がなかったことを意味します。その場合は --help を使うか、より具体的な問い合わせに切り替えてください。

レシピ

検索可能なコーパスを構築する
substack-reader-pp-cli archive astralcodexten --limit 0 && substack-reader-pp-cli search "prediction markets"

出版物全体をミラーリングし(--limit 0 は上限なし)、その後 FTS のランキングを使ってオフラインで検索します。コンパクト形式/エージェント検索のヒットには、主要なフィールドと長さを制限した snippet が含まれます。投稿本文全体は含まれません。ヒットの全文が必要な場合は read(または --select)を使ってください。

大きな投稿から必要なフィールドを絞り込む
substack-reader-pp-cli read astralcodexten/open-thread-441 --agent --select title,post_date,audience,body_html

冗長な投稿オブジェクトから、エージェントに必要なフィールドだけを取得します。

複数の投稿を1回の呼び出しで読む
substack-reader-pp-cli read astralcodexten/slug-one astralcodexten/slug-two astralcodexten/slug-three --agent --select slug,subtitle,audience

シェルループを使わずにスラッグのリストをまとめて読み取ります。JSON モードでは投稿エンベロープの配列が返され、無効なスラッグが1つあっても、ほかの結果には影響しません。

読者層の構成分析
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キーも、設定が必要な項目もありません。認証が関係するのは有料投稿のみです。

任意の方法が1つあります。自分のSubstackのセッションCookie(substack.sid)です。Substackは提示されたセッションに基づいてサーバー側でアクセス権を判定するため、自分のCookieを渡すと、すでに購読している有料投稿だけが読めるようになります。それ以外は読めません。これは自分のCookieであり、APIキーではありません。無料コンテンツには一切必要なく、アカウント全体の購読が対象になるため、Cookieはアカウント内のすべての購読に使えます(アクセス権は出版物ごとではなく、アカウント単位です)。

次の3つの方法のいずれかで指定できます(最初に見つかった方法が優先されます)。

# 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を1文字取り違えるだけで、Cookieが気づかれないまま匿名として扱われます。

MCP サーバーを使う場合。 MCP サーバーはCLIと同じ方法でセッションを解決するため、設定ディレクトリ内のCookieファイルにはenvブロックが一切不要です。cookie.jsonを保存すれば、サーバーが読み込みます。明示的に設定する場合は、Claude Desktopの設定でenvの下にいずれかの変数を設定してください(MCPBバンドルでは、任意項目として入力を求められます)。

Cookieはお客様の管理下にあります。 Cookieがこのマシンから送信されるのはSubstackに対してのみです。スクリプト実行で漏えいしないよう、あらゆる出力でマスキングされ、再配布されることもありません。CLIがCookieを保存する場合は、親ディレクトリを0700、ファイルを0600に設定します。また、グループまたはその他のユーザーが読み取り可能なCookieファイルには警告が表示されます。

失敗は正直に通知され、黙って処理されることはありません。 セッションがない状態で有料投稿を取得すると、Substackの公開プレビューが返され、その旨が示されます。access: preview、authenticated: false、full: falseに加えて、標準エラー出力にpreview only (N of ~M words) — no session configuredの行が表示されます。ダウングレードは常に明示され、全文を取得したかのように扱われることはありません。

substack-reader-pp-cli doctorを実行して設定を確認してください。

エージェントモード

任意のコマンドに--agentを追加します。次のオプションが有効になります:--json --compact --no-input --no-color --yes。

  • パイプに対応 — 標準出力にJSON、標準エラー出力にエラーを出力します

  • フィルタリング可能 — --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を参照してください。人間が読みやすいN results (live)という概要は、標準出力が端末に接続されており、かつ機械処理用フォーマットのフラグ(--json、--csv、--compact、--quiet、--plain、--select)が指定されていない場合に限り、標準エラー出力に表示されます。パイプやエージェント経由で利用する場合、および明示的にフォーマットを指定した場合は、標準出力に純粋なJSONが出力されます。

パスと状態

エージェントは、CLI のパス解決をランタイム契約の一部として扱う必要があります。

  • 1 回の実行では --home <dir> を使用するか、SUBSTACK_READER_HOME=<dir> を設定して、4 種類すべてのパスを 1 つのルート配下に移動します。

  • 特定の種類だけを別の場所にする必要がある場合に限り、種類ごとの環境変数を使用します: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-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 は 1 回の実行に限って使用します。環境変数の設定を解除しても、移動は元に戻りません。SUBSTACK_READER_HOME を解除する前に手動でファイルを移動してください。そうしないと、doctor は以前のルートに残された認証情報を見つけられなくなります。

自動学習

この CLI には、自動記録型の学習ループが組み込まれています。CLI は独自に記録を管理します。すべての実行はローカルに記録され、失敗したフラグを修正して再試行すると flag_alias 候補が自動生成され、プレイブックのないクエリファミリーに対して teach を実行すると、セッションの記録から playbook_candidate が自動生成されます。あなたが判断するのは、recall を最初に実行し、提示された候補に基づいて対応し、最終回答を teach し、修正に気づいたら playbook amend を実行することだけです。失敗を手動で記録する必要はありません。

ステップ 1:探索の前に recall

新しいユーザーの質問について、list/search/drill コマンドを実行する前に、次を実行します。

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> は派生シグネチャを tombstone 化し、同じ候補が再表示されないようにします。エンベロープには、今対応する価値のある候補だけが含まれます。未処理候補の全件は 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>(トップレベル):構造的に一致する行は存在しますが、その正規エンティティはライブクエリのものとは異なります。コールドスタートとして扱われます。この警告には、ヒントとして競合する正規名が含まれますが、その行は「結果」に昇格されません。
  • ambiguous_alias(トップレベル):単一のクエリエンティティが複数の正規名に解決されました(例:「Cards」→ Arizona Cardinals + St. Louis Cardinals)。リソースを決定する前に、コンテキストから曖昧さを示してください。
  • candidates_present(トップレベル):エンベロープに candidates セクションが含まれています。まずステップ 2 の候補分岐で処理してください。
  • lookup_refresh_available(トップレベル):クエリ内のエンティティにルックアップ行がまだありませんが、同期済みデータから作成できる可能性があります。substack-reader-pp-cli sync を実行して、エンティティのルックアップを更新してください。
  • トップレベルの no_learnings_for_query_family:テーブルに Jaccard の下限値を上回る行がありませんでした。完全なコールドスタートです。
ステップ 4:回答を確定したら、必ず teach & を実行する

学習は無条件です。ストアでは回答できなかったクエリを解決した後、最終的なリソースの対応関係をバックグラウンドで学習させてください。呼び出し回数のしきい値も、学習する「価値」があったかどうかの判断も必要ありません。学習はこのループの要です。プレイブックのないファミリーではプレイブックの合成を開始し、同じ対象を指す言い回しは一つのファミリーにまとめられるため、ほぼ重複する学習内容によってストアが分断されることはありません。ユーザー向けの回答を組み立てた後、出力する前に、シェルの & を付けて実行し、呼び出しがすぐに戻るようにしてください。

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 では表面化しなかった回避策(フラグが黙って破棄される、未公開のパラメーター、ページネーションの工夫、ペイロードに関する注意点)です。統合されたワンコール形式を優先してください。リソースの学習と playbook を、同じ teach 呼び出しで記録します。

# 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 ファイルは、steps、entity_slots、expected_tool_calls を含む JSON です。Notes ファイルは、注意点をそのまま記載する Markdown です。ファイルを使わない呼び出し元(MCP のみを使うエージェント)は、同じ内容をインラインで渡します。統合された teach 形式では --playbook-json と --playbook-notes を、teach-playbook では --playbook-json と --notes を使用します。統合された teach 形式では playbook フラグは任意です。リソースのみを teach する場合は、フラグをすべて省略してください。単独の 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} でラップされている、ペイロードがドキュメントの記載より 2 段深くネストされているなど)。
  • 観察されたスキーマの変化(フィールド名が変更された、シーズン間でインデックスがずれた、API がカテゴリラベルを小文字で返すようになったなど)。

notes に含めてはいけないもの:

  • ユーザーの質問に対する年やエンティティ固有の回答。それは応答であり、学習内容ではありません。
  • playbook が実行時にすでに取得する、チーム、選手、行ごとのデータ。
  • 既存の notes の内容を言い換えただけの記述。

amend コマンドは、ファミリーの既存の notes にタイムスタンプ付きのマーカー([amend YYYY-MM-DDTHH:MMZ]: <text>)を付けて追記します。複数の amend が蓄積され、監査履歴を確認できます。そのファミリーに playbook がまだない場合、amend は notes のみの playbook を作成するため、初回の修正も確実に反映されます。

amend notes における PII の取り扱い

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 の書き込み処理の両方が短絡されます。蓄積された学習内容の影響を受けてはならない、決定的なエージェントの処理やテストで使用してください。
  • 環境変数に SUBSTACK_READER_NO_LEARN=true を設定すると、パイプラインが全体で無効になります。

エージェントからのフィードバック

この 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 されることはありません。デフォルトの動作はローカルのみです。

バグ報告ではなく、何に意外性を感じたかを書いてください。短く、具体的に、1 行で記録してください。積み重なるのはその部分です。

出力の配信

すべてのコマンドで --deliver <sink> を指定できます。出力は stdout に加えて(または stdout の代わりに)指定した sink に送られるため、エージェントはコマンドの結果を手作業でパイプ処理せずに振り分けられます。対応している sink は 3 種類です。

Sink効果
stdoutデフォルト。stdout のみに書き込みます
file:<path>出力を <path> にアトミックに書き込みます(一時ファイル+rename)
webhook:<url>出力本文を URL に POST します(--compact 指定時は application/json または application/x-ndjson)

不明なスキームは、対応するスキームを示す構造化エラーとともに拒否されます。Webhook の失敗時はゼロ以外の終了コードを返し、URL と HTTP ステータスを stderr に記録します。

名前付きプロファイル

プロファイルは、保存済みのフラグ値のセットで、呼び出しをまたいで再利用できます。スケジュール実行や定期実行で、実行ごとに異なる入力を受け取りながら同じ保存済みフラグを再利用するエージェントで使用してください。

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 に一覧表示するため、情報を取得するエージェントは実行時にそれらを見つけられます。

終了コード

Code意味
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。

コメント